当前位置: 首页 > 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.dtcms.com/a/277520.html

相关文章:

  • Qt:编译qsqlmysql.dll
  • React强大且灵活hooks库——ahooks入门实践之常用场景hook
  • NoSQL 介绍
  • day052-ansible handler、roles与优化
  • Spring AI 项目实战(十七):Spring + AI + 通义千问星辰航空智能机票预订系统(附完整源码)
  • SDN软件定义网络架构深度解析:分层模型与核心机制
  • Datawhale AI 夏令营【更新中】
  • java虚拟线程
  • 面试150 从中序与后序遍历构造二叉树
  • Maven项目没有Maven工具,IDEA没有识别到该项目是Maven项目怎么办?
  • html案例:编写一个用于发布CSDN文章时,生成有关缩略图
  • 【拓扑排序+dfs】P2661 [NOIP 2015 提高组] 信息传递
  • 线下门店快速线上化销售四步方案
  • 在i.MX8MP上如何使能BlueZ A2DP Source
  • 如何设计高并发架构?深入了解高并发架构设计的最佳实践
  • Nature子刊 |HERGAST:揭示超大规模空间转录组数据中的精细空间结构并放大基因表达信号
  • DETRs与协同混合作业训练之CO-DETR论文阅读
  • Pandas 的 Index 与 SQL Index 的对比
  • Flask中的路由尾随斜杠(/)
  • SQL140 未完成率top50%用户近三个月答卷情况
  • react中为啥使用剪头函数
  • (nice!!!)(LeetCode 面试经典 150 题 ) 30. 串联所有单词的子串 (哈希表+字符串+滑动窗口)
  • win10 离线安装wsl
  • 论文翻译:Falcon: A Remote Sensing Vision-Language Foundation Model
  • 26-计组-数据通路
  • 楼宇自动化:Modbus 在暖通空调(HVAC)中的节能控制(一)
  • Linux驱动开发1:设备驱动模块加载与卸载
  • java+vue+SpringBoo中小型制造企业质量管理系统(程序+数据库+报告+部署教程+答辩指导)
  • 查看Linux服务器显卡使用情况的详细教程
  • win11添加无线显示器(两个笔记本实现双屏)