Fast Power

快速乘法与快速幂

在编程竞赛和数学计算中,处理大数经常会遇到一个关键问题:溢出。即使使用C++的long long类型(大约能存储9.2e18的值),大数相乘或计算高次幂也很容易超出这个限制。快速乘法并非传统意义上的"快速",而是为处理大数值提供了安全的替代方案。

快速乘法:通过分解实现安全计算

快速乘法(也称为二进制乘法)是一种计算(a * b) % mod而不会溢出的技术,即使ab接近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)\)时间,这对于大指数至关重要
  • 两者结合形成了强大的组合,用于安全地进行大数的模运算
posted @ 2025-08-29 22:20  wz150432  阅读(7)  评论(0)    收藏  举报