当前位置: 首页 > news >正文

【基础算法】倍增

倍增思想

倍增,顾名思义就是翻倍。它能够使线性的处理转化为对数级的处理,极大地优化时间复杂度。


一. 快速幂算法

1. 什么是快速幂

  • level 1

    当我们要计算 a64a^{64}a64 时,我们可以一个一个算的方式即:a×a×⋯aa\times a\times\cdots aa×a×a,这样算 64 次即可得出答案,但是当数很大时,我们这个做法显然是会超时的。

  • level 2

    于是,我们可以利用幂运算的性质,即:

a1×a1=a2a2×a2=a4⋮a32×a32=a64a^1\times a^1=a^2\\ a^2\times a^2=a^4\\ \vdots\\ a^{32}\times a^{32}=a^{64} a1×a1=a2a2×a2=a4a32×a32=a64

​ 这样的话,我们只需要算 6 次就可以快速得到结果了,我们通过一个翻倍的过程,将时间复杂度直接从 O(n)O(n)O(n) 优化到了 O(log⁡N)O(\operatorname{log}N)O(logN)

  • level 3

    但是,a64a^{64}a64 这个例子有点特殊了,我们发现它的指数,也就是 ana^nan 中的 nnn,恰好是 2 的幂。那如果不是呢?比如 a105a^{105}a105
    注意到,虽然 105 不是 2 的幂,但是它可以拆解成几个 2 的幂的和的形式:
    105=1+8+32+64105=1+8+32+64 105=1+8+32+64
    于是,通过指数运算的性质,我们有:
    a105=a1+8+32+64=a1×a8×a32×a64\begin{aligned} a^{105}&=a^{1+8+32+64}\\ &=a^1\times a^8\times a^{32}\times a^{64}\end{aligned} a105=a1+8+32+64=a1×a8×a32×a64

    这样的话,我们就可以利用 level 2 中计算出的结果来求出 a105a^{105}a105 了,那么我们是如何得到 105=1+8+32+64105=1+8+32+64105=1+8+32+64 的呢?

  • level 4

    这时候就要用到我们的二进制了,我们高中的时候都学过一个数表示成二进制的转换方法。例如:二进制数 1011,它的值在 10 进制中可以表示为 1×20+1×21+0×22+1×231\times2^0+1\times2^1+0\times2^2+1\times2^31×20+1×21+0×22+1×23,也就是 11。

    通过这个转换方法,我们就可以把 aaa 的指数 nnn 转换成为 2k2^k2k 的序列之和的形式。具体这个 2k2^{k}2k 是乘 0 还是乘 1,这就看对应的二进制位上是 0 还是 1 了。

以上的过程就是倍增思想中的快速幂算法。


2. 模运算的性质与技巧

上面的快速幂算法从时间上解决了计算某一个数的某次方的过程,但是如果的底数和指数都非常大,最终结果连 long long 都存不下的时候,我们往往会对结果进行取模,也就是通常题目会让你计算 abmodpa ^ {b}\bmod pabmodp 的结果。 但是我们在计算 aba^{b}ab 的过程中就有可能超出存储范围,这个时候我们不能等到算完再取模,而是要在算的过程中边算边取模。下面介绍模运算的几个性质:

(1)当计算过程中只有 “加法” 和 “乘法” 的时候,取模可以放在任意的位置。

也就是说,如果我们计算
(a×b×c×d)modp(a\times b \times c \times d) \bmod p (a×b×c×d)modp
时,它的结果等同于
(((((a×b)modp)×c)modp)×d)modp(((((a\times b)\bmod p) \times c)\bmod p) \times d) \bmod p (((((a×b)modp)×c)modp)×d)modp
也等同于
((amodp)×(bmodp)×(cmodp)×(dmodp))modp((a\bmod p) \times (b\bmod p) \times (c\bmod p) \times (d\bmod p)) \bmod p ((amodp)×(bmodp)×(cmodp)×(dmodp))modp
(2)当计算过程中存在减法时,结果可能是负数,此时如果需要补正则需要使用 “模加模” 的技巧。

也就是当我们计算
(a−b)modp(a-b)\bmod p (ab)modp
时,bbb 有可能大于 aaa,此时结果是一个负数,那么我们可以对 a−ba - bab 先模 ppp,再加上一个 ppp 变成正数,再取模:
((a−b)modp+p)modp((a - b)\bmod p + p)\bmod p ((ab)modp+p)modp


3. 【模板】快速幂 ⭐

【题目链接】

P1226 【模板】快速幂 - 洛谷

【题目描述】

给你三个整数 a,b,pa,b,pa,b,p,求 abmodpa^b \bmod pabmodp

【输入格式】

输入只有一行三个整数,分别代表 a,b,pa,b,pa,b,p

【输出格式】

输出一行一个字符串 a^b mod p=s,其中 a,b,pa,b,pa,b,p 分别为题目给定的值, sss 为运算结果。

【示例一】

输入

2 10 9

输出

2^10 mod 9=7

【说明/提示】

样例解释

210=10242^{10} = 1024210=10241024mod9=71024 \bmod 9 = 71024mod9=7

数据规模与约定

对于 100%100\%100% 的数据,保证 0≤a,b<2310\le a,b < 2^{31}0a,b<231a+b>0a+b>0a+b>02≤p<2312 \leq p \lt 2^{31}2p<231

#include<iostream>using namespace std;typedef long long LL;LL a, b, p;// 快速幂模板
LL q_pow(LL a, LL b, LL p)
{LL res = 1;while(b){// 如果指数对应的二进制的当前位为 1if(b & 1) res = res * a % p;a = a * a % p;b >>= 1;}return res;
}int main()
{cin >> a >> b >> p;printf("%lld^%lld mod %lld=%lld", a, b, p, q_pow(a, b, p));return 0;
}

二、六十四位整数乘法 ⭐

【题目链接】

P10446 64位整数乘法 - 洛谷

【题目描述】

aaabbbppp 取模的值。

【输入格式】

第一行输入整数 aaa,第二行输入整数 bbb,第三行输入整数 ppp

【输出格式】

输出一个整数,表示 a*b mod p 的值。

【示例一】

输入

3
4
5

输出

2

【说明/提示】

1≤a,b,p≤10181 \le a,b,p \le 10^{18}1a,b,p1018


1. 解题思路

这道题与快速幂算法思路几乎一样,a×ba\times ba×b 本质上就是 bbbaaa 相加。直接相乘或者先模再乘都是会溢出的,我们也不可能真的就写一个循环来循环 bbb 次,这个时候就要用到倍增的思想。

一个数通过它的二进制可以表示成为多个 2 的幂相加的形式,比如
13×11=13×(1×20+1×21+0×22+1×23)=13×1+13×2+13×0+13×8\begin{aligned} 13 \times 11 &= 13 \times (1\times2^0+1\times2^1+0\times2^2+1\times2^3)\\&= 13 \times 1 + 13 \times 2 + 13 \times 0 + 13 \times 8 \end{aligned} 13×11=13×(1×20+1×21+0×22+1×23)=13×1+13×2+13×0+13×8
那么这个本来需要循环 11 次的运算就变成了只需要 4 次,大大降低了时间复杂度。原理就是把 “累加次数” 不断加倍,这样计算的话,不但时间复杂度低,边计算边取模就不会溢出了。


2. 代码实现

#include<iostream>using namespace std;typedef long long LL;LL a, b, p;LL solve(LL a, LL b, LL p)
{LL res = 0;while(b){// 如果 b 对应的二进制当前位为 1if(b & 1) res = (res + a) % p;a = (a + a) % p;b >>= 1;}return res;
}int main()
{cin >> a >> b >> p;cout << solve(a, b, p) << endl;return 0;
}
http://www.xdnf.cn/news/1115569.html

相关文章:

  • 【开源项目】拆解机器学习全流程:一份GitHub手册的工程实践指南
  • 从儿童涂鸦到想象力视频:AI如何重塑“亲子创作”市场?
  • ABP VNext + 多级缓存架构:本地 + Redis + CDN
  • Linux的 iproute2 配置:以太网(Ethernet)、绑定(Bond)、虚拟局域网(VLAN)、网桥(Bridge)笔记250713
  • Prometheus 第一篇:快速上手
  • Vue配置特性(ref、props、混入、插件与作用域样式)
  • 第三章-提示词-解锁Prompt提示词工程核销逻辑,开启高效AI交互(10/36)
  • Linux|服务器|二进制部署nacos(不是集群,单实例)(2025了,不允许还有人不会部署nacos)
  • 学习C++、QT---23(QT中QFileDialog库实现文件选择框打开、保存讲解)
  • DVWA靶场通关笔记-XSS DOM(Medium级别)
  • 教程:如何查看浏览器扩展程序的源码
  • 飞算 JavaAI 智能编程助手:颠覆编程旧模式,重构开发生态
  • 闲庭信步使用图像验证平台加速FPGA的开发:第十三课——图像浮雕效果的FPGA实现
  • JAVA生成PDF(itextpdf)
  • 互联网大厂Java面试:从Spring Boot到微服务的场景应用
  • HTML 初体验
  • HarmonyOS组件/模板集成创新活动-元服务小云体重管理引入案例(步骤条UI组件)
  • HarmonyOS组件/模板集成创新活动-开发者工具箱
  • 【设计模式】备忘录模式(标记(Token)模式)
  • 为什么玩游戏用UDP,看网页用TCP?
  • 融合开源AI大模型与MarTech:AI智能名片与S2B2C商城小程序源码赋能数字化营销新生态
  • 【QT】使用QSS进行界面美化
  • 【Linux | 网络】应用层
  • Rust赋能文心大模型4.5智能开发
  • Leetcode 3615. Longest Palindromic Path in Graph
  • 操作系统-第四章存储器管理和第五章设备管理-知识点整理(知识点学习 / 期末复习 / 面试 / 笔试)
  • 笔记/sklearn中的数据划分方法
  • 滑动窗口-76.最小覆盖子串-力扣(LeetCode)
  • 【保姆级图文详解】MCP架构(客户端-服务端)、三种方式使用MCP服务、Spring AI MCP客户端和服务端开发、MCP部署方案、MCP安全性
  • 【Datawhale夏令营】用AI做带货视频评论分析