1. 快速幂算法在信息学竞赛中的核心价值
第一次接触信息学竞赛的选手,往往会在简单的幂运算题目上栽跟头。记得我当年参加省赛时,就遇到过计算a^b mod p的题目,当时用最朴素的循环乘法,结果在测试用例b=10^18时直接超时。这就是快速幂算法存在的意义——它能把O(n)的时间复杂度降到O(logn),让大数幂运算变得可行。
快速幂(Fast Exponentiation)本质上是一种利用二分思想优化幂运算的算法。它的核心在于将指数进行二进制分解,通过不断平方和相乘的方式减少计算次数。比如计算3^13,传统方法需要13次乘法,而快速幂只需要5次(3^1→3^2→3^4→3^8→合并为3^13)。
关键提示:在信息学竞赛中,快速幂最常见的应用场景包括:
- 大数取模运算(如RSA加密)
- 矩阵快速幂解递推问题
- 组合数学中的模逆元计算
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 快速幂的数学原理与实现解析
2.1 算法数学基础
快速幂基于以下两个数学性质:
- 幂的分解性质:a^b = a^(b1+b2) = a^b1 × a^b2
- 模运算性质:(a × b) mod p = [(a mod p) × (b mod p)] mod p
以计算5^13 mod 7为例:
- 将13转为二进制1101
- 5^13 = 5^(8+4+1) = 5^8 × 5^4 × 5^1
- 计算过程:
5^1 mod 7 = 5
5^2 mod 7 = (5×5) mod 7 = 4
5^4 mod 7 = (4×4) mod 7 = 2
5^8 mod 7 = (2×2) mod 7 = 4 - 最终结果:4×2×5 mod 7 = 40 mod 7 = 5
2.2 标准实现模板
cpp复制typedef long long ll;
ll fastPow(ll a, ll b, ll mod) {
ll res = 1;
while (b > 0) {
if (b & 1) res = (res * a) % mod;
a = (a * a) % mod;
b >>= 1;
}
return res;
}
代码解析:
- 初始化res=1(任何数的0次方为1)
- 循环条件b>0(处理完所有二进制位)
- b & 1判断当前最低位是否为1
- 无论当前位是否为0,a都需要平方(对应二进制位的权重)
- b右移一位处理下一个二进制位
3. 竞赛中的典型应用场景
3.1 大数取模问题
例题:计算a^b mod p (1 ≤ a,b ≤ 10^18, 1 ≤ p ≤ 10^9)
cpp复制#include <iostream>
using namespace std;
int main() {
long long a, b, p;
cin >> a >> b >> p;
cout << fastPow(a, b, p);
return 0;
}
注意事项:
- 使用long long防止中间结果溢出
- 初始时先对a取模:a %= p
- 特判p=1时结果必为0
3.2 矩阵快速幂解递推
以斐波那契数列为例,将O(n)的DP优化为O(logn):
cpp复制struct Matrix {
ll m[2][2];
Matrix() { memset(m, 0, sizeof(m)); }
};
Matrix multiply(Matrix a, Matrix b, ll mod) {
Matrix res;
for(int i=0; i<2; ++i)
for(int j=0; j<2; ++j)
for(int k=0; k<2; ++k)
res.m[i][j] = (res.m[i][j] + a.m[i][k]*b.m[k][j]) % mod;
return res;
}
Matrix fastMatrixPow(Matrix a, ll b, ll mod) {
Matrix res;
res.m[0][0] = res.m[1][1] = 1; // 单位矩阵
while(b > 0) {
if(b & 1) res = multiply(res, a, mod);
a = multiply(a, a, mod);
b >>= 1;
}
return res;
}
ll fibonacci(ll n, ll mod) {
if(n == 0) return 0;
Matrix mat;
mat.m[0][0] = mat.m[0][1] = mat.m[1][0] = 1;
mat = fastMatrixPow(mat, n-1, mod);
return mat.m[0][0];
}
4. 常见错误与优化技巧
4.1 易错点排查表
| 错误类型 | 示例 | 修正方案 |
|---|---|---|
| 中间结果溢出 | res = res * a |
先取模:res = (res * a) % mod |
| 负数指数处理 | fastPow(2, -3) | 转换为正指数求逆元 |
| 零的零次方 | fastPow(0, 0) | 特殊判断返回1或报错 |
| 模数为1 | fastPow(a,b,1) | 直接返回0 |
4.2 性能优化实践
- 循环展开优化:对于固定大小的模数(如1e9+7),可以展开内层循环
cpp复制ll fastPowOpt(ll a, ll b) {
const ll mod = 1e9+7;
ll res = 1;
a %= mod;
while(b) {
if(b & 1) res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res;
}
- 编译器内置函数:GCC的__builtin_clz可以加速二进制位处理
cpp复制int leading_zeros = __builtin_clzll(b);
for(int i=63-leading_zeros; i>=0; --i) {
if((b >> i) & 1) ...
}
- 预处理幂表:当需要多次计算相同底数的幂时
cpp复制vector<ll> powTable(ll a, ll max_exp, ll mod) {
vector<ll> res(max_exp+1);
res[0] = 1;
for(int i=1; i<=max_exp; ++i)
res[i] = res[i-1] * a % mod;
return res;
}
5. 扩展应用与变种算法
5.1 快速幂求模逆元
在组合数计算中,利用费马小定理:
当p为质数时,a^(p-1) ≡ 1 mod p ⇒ a^(p-2) ≡ a^(-1) mod p
cpp复制ll modInverse(ll a, ll p) {
return fastPow(a, p-2, p);
}
5.2 快速乘优化
当模数接近long long上限时(如1e18),需要用快速乘防止溢出:
cpp复制ll fastMul(ll a, ll b, ll mod) {
ll res = 0;
while(b > 0) {
if(b & 1) res = (res + a) % mod;
a = (a << 1) % mod;
b >>= 1;
}
return res;
}
5.3 多精度快速幂
适用于需要精确值的场景(如计算最后k位数字):
python复制def bigPow(a, b):
res = 1
while b > 0:
if b % 2 == 1:
res *= a
a *= a
b //= 2
return res
在实际竞赛中,我建议准备三个版本的快速幂模板:标准版、矩阵版和快速乘版。根据题目特点选择合适版本,可以节省大量编码时间。记住,快速幂不仅是工具,更体现了分治思想的核心——将大问题分解为小问题解决。这种思想在解决更复杂的竞赛题目时同样适用。
