Fast Power
快速乘法与快速幂
在编程竞赛和数学计算中,处理大数经常会遇到一个关键问题:溢出。即使使用C++的long long类型(大约能存储9.2e18的值),大数相乘或计算高次幂也很容易超出这个限制。快速乘法并非传统意义上的"快速",而是为处理大数值提供了安全的替代方案。
快速乘法:通过分解实现安全计算
快速乘法(也称为二进制乘法)是一种计算(a * b) % mod而不会溢出的技术,即使a和b接近long long的限制。
常规乘法的问题
直接相乘大数值会失败,因为中间结果超出了long long的存储容量:
long long a = 1e18;
long long b = 1e18;
long long product = (a * b) % 1000000007; // 在取模之前就发生溢出!
这段代码会产生错误结果,因为a * b(1e36)在进行模运算之前就溢出了long long。
快速乘法的工作原理
快速乘法不是直接相乘,而是将操作分解为加法和位移运算,在每一步都应用模运算以保持数值较小:
#include <iostream>
using namespace std;
const int p = 1e9 + 7;
int mul(int a, int b) {
int res = 0;
for ( ; b ; b >>= 1) {
if (b & 1) res = (long long)(res + a) % p;
a = (long long)(a + a) % p;
}
return res;
}
int main() {
long long a, b;
cin >> a >> b;
cout << mul(a % p, b % p);
return 0;
}
关键特性:
- 时间复杂度:\(O(log b)\) - 比直接乘法慢但安全
- 目的:防止大数在模运算下相乘时溢出
- 权衡:为了大数值的安全性而牺牲速度
快速幂:高效求幂运算
快速幂(或平方求幂法)高效地计算(a^b) % mod,将时间复杂度从\(O(b)\)降低到\(O(log b)\)。当与快速乘法结合时,它成为安全处理极大指数的强大工具。
朴素求幂法的局限性
计算a^b的朴素方法需要重复乘法,对于大指数来说效率低下且存在溢出风险:
int naive_pow(int a, int b, int p) {
for (int i = 1; i <= b; i ++ )
a = (long long) a * a % p;
return a;
}
快速幂的工作原理
快速幂利用指数的数学特性来减少乘法次数:
每个正整数都可以表示为2的幂次之和,这本质上是它的二进制形式。例如,13的二进制是1101,转换为:
$13 = 8 + 4 + 1 = 2^3 + 2^2 + 2^0 $
当计算基数的这个指数次幂时(例如\(a^{13}\)),我们使用指数性质\(a^{x+y} = a^x × a^y\)。这让我们可以用二进制分量重写表达式:
\[a^{13} = a^{8+4+1} = a^8 × a^4 × a^1
\]
关键洞察是每一项 \(a^1, a^2, a^4, a^8,\cdots\) 都是前一项的平方。通过遍历指数二进制形式的每一位,我们可以即时计算这些项,并且只有当相应位被设置(即等于1)时才将它们乘入结果。
#include <iostream>
using namespace std;
const int p = 1e9 + 7;
typedef long long ll;
int qpow(int a, int b) {
int res = 1;
for ( ; b ; b >>= 1) {
if (b & 1) res = (ll)(res * a) % p;
a = (ll)(a * a) % p;
}
return res;
}
int main() {
int a, b;
cin >> a >> b;
cout << qpow(a, b);
return 0;
}
关键特性:
- 时间复杂度:\(O(log b)\) - 比朴素方法快得多
- 目的:在模运算下高效计算大幂次
- 优势:与快速乘法结合使用时,消除溢出风险
结论
- 快速乘法将\(O(1)\)时间换为\(O(log b)\)时间,以防止大数相乘时的溢出
- 快速幂将求幂运算从\(O(n)\)时间减少到\(O(log n)\)时间,这对于大指数至关重要
- 两者结合形成了强大的组合,用于安全地进行大数的模运算

浙公网安备 33010602011771号