快速幂
快速幂把指数拆成二进制:当前位是 `1` 就乘进答案,底数每轮自乘一次。
一句话算法
快速幂把指数拆成二进制:当前位是 1 就乘进答案,底数每轮自乘一次。
问题模型
给定整数
如果直接做
快速幂利用指数的二进制表示,把乘法次数降到
核心直觉
任意非负整数
所以:
而这些
因此我们只需要从低位到高位扫描
- 当前二进制位是
1:把当前base乘进答案。 - 每扫描一位:
base = base * base,进入下一位的权值。
“口诀”
是 1 就乘,base 自乘,指数右移。
算法步骤
- 令
ans = 1 % mod。 - 令
base = base % mod。 - 当
exp > 0:- 如果
exp & 1,说明当前最低位是1,执行ans = ans * base % mod。 - 执行
base = base * base % mod。 - 执行
exp >>= 1,删掉已经处理的最低位。
- 如果
- 返回
ans。
算法证明
核心不变量:每一轮开始时,尚未处理的指数部分仍然由 exp 表示,已经处理且需要保留的幂已经乘进 ans。
设原指数为:
其中
-
第
轮时, base等于: -
若第
位 ,答案中必须包含这一项,所以乘入: -
若第
位 ,答案中不需要这一项,跳过即可。 -
每一轮后:
所以
base = base * base正确进入下一位。
所有二进制位处理完后,ans 中正好乘入了所有
算法正确。
复杂度分析
- 时间复杂度:
。 - 空间复杂度:
。
如果 base * base 可能超过 long long,需要使用 __int128 或快速乘。
代码实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
// 计算 base^exp mod mod,要求 mod > 0。
ll quick_pow(ll base, ll exp, ll mod) {
ll ans = 1 % mod;
base %= mod;
while (exp > 0) {
if (exp & 1) ans = ans * base % mod;
base = base * base % mod;
exp >>= 1;
}
return ans;
}
int main() {
ll base, exp, mod;
cin >> base >> exp >> mod;
cout << quick_pow(base, exp, mod) << "\n";
return 0;
}
测试用例
输入:
2 13 1000
输出:
192
解释:
应用分类详解
快速幂的本质是:把“重复乘很多次”变成“按二进制选择若干个平方项”。只要题目出现大指数、重复合成、幂取模,就应该想到快速幂。
一、大指数幂取模
典型模式: 计算
识别信号: 题面出现“幂”“取模”“指数很大”“
核心建模: 用二进制拆指数,每一位只处理一次。
| 应用场景 | 经典题目 | 核心思路 |
|---|---|---|
| 模板计算 | luogu-P1226 | 快速幂直接计算幂取模 |
| 大指数取模 | LeetCode 50 | 不取模时同样使用二进制拆指数 |
二、数论公式中的幂
典型模式: 公式里包含
识别信号: 出现“费马小定理”“逆元”“组合数取模”“矩阵快速幂前置知识”。
核心建模: 把公式中的幂计算交给快速幂。
| 应用场景 | 经典题目 | 核心思路 |
|---|---|---|
| 费马逆元 | luogu-P3811 | 质数模下 |
| 组合数取模 | luogu-P3807 | Lucas 或阶乘逆元常用快速幂 |
三、重复操作的快速合成
典型模式: 一个操作需要重复执行
识别信号: 出现“重复执行很多次”“函数复合”“状态转移很多步”。
核心建模: 把乘法换成更一般的“合并操作”。如果操作满足结合律,就可以按二进制倍增。
| 应用场景 | 经典题目 | 核心思路 |
|---|---|---|
| 矩阵快速幂 | luogu-P1962 | 把数乘换成矩阵乘 |
| 置换快速幂 | luogu-P3390 | 重复变换可按二进制合成 |
经典例题
1. luogu-P1226
快速幂模板题。直接套用 quick_pow(base, exp, mod)。
2. luogu-P3811
线性逆元是更优解,但快速幂求单个逆元是必须掌握的基础方法。
3. luogu-P1962
斐波那契数列的矩阵快速幂做法。本质仍然是“指数二进制拆分”,只是乘法对象从整数变成矩阵。
参考
- 旧版文章:
Rbook_ejs_old/book/math/quick_pow/index.md