1. 什么是快速幂算法?

快速幂算法,也称为二进制取幂法,是一种高效计算大数幂的算法。它通过将指数进行二进制分解,将幂运算的时间复杂度从O(n)降低到O(log n),在处理大数幂运算时优势尤为明显。

2.传统幂运算的问题

例如:传统计算aⁿ的方法是进行n-1次乘法

long long malpow(long long a, long long n) {
    long long result = 1;
    for (long long i = 0; i < n; ++i) {
        result *= a;
    }
    return result;
}

虽然这种方法的时间复杂度是O(n),但是当n很大时(如n=1e18),很容易发生溢出问题导致运行代码失败。

补充:幂运算时的溢出问题(在计算过程中,中间结果可能会迅速增大。当结果超出数据类型所能表示的范围时,就会发生溢出,导致计算结果错误。)

基于传统幂运算的劣势做出改进就产生了——快速幂算法,

性能比较

快速幂算法虽然也可能会出现溢出问题,但由于其计算步骤较少,中间结果相对较小,出现溢出的概率相对较低。而且在实际应用中,可以结合取模运算进一步避免溢出问题。

快速幂算法建立在以下数学原理之上:

幂的乘法法则:aᵐ⁺ⁿ = aᵐ × aⁿ
幂的幂法则:(aᵐ)ⁿ = aᵐⁿ
二进制分解:任何整数可以表示为2的幂次和

3.快速幂代码实现

基于以下数学原理实现快速幂算法

 aⁿ = (a²)ⁿ/² (n为偶数)
 aⁿ = a × aⁿ⁻¹ (n为奇数)

一.快速幂的递归实现

long long fast_pow(long long a, long long n) {
    if (n == 0) return 1;
    if (n % 2 == 1) return a * fast_pow(a, n - 1);
    long long temp = fast_pow(a, n / 2);
    return temp * temp;
}

二.快速幂的迭代实现

相比于递归实现的方式迭代实现更高效

(1)普通迭代实现

long long fast_pow(long long a, long long n) {
    long long result = 1;
    while (n > 0) {
        if (n % 2 == 1) {  // 判断是否为奇数
            result *= a;
        }
        a *= a;      // a平方
        n /= 2;      // 除以2
    }
    return result;
}

 (2)位运算迭代实现

long long fast_pow(long long a, long long n) {
 long long result = 1;
    while (n > 0) {
        if (n & 1) {  // 判断二进制最低位是否为1
            result *= a;
        }
        a *= a;      // a平方
        n >>= 1;     // 右移一位(相当于n /= 2)
    }
    return result;
}

以计算5¹³为例(13的二进制为1101)计算步骤如下:

long long fastPow(long long a, long long n) {
    long long res = 1;
    while (n) {
        if (n & 1) res *= a; // 判断最低位是否为1
        a *= a;              // a平方
        n >>= 1;             // 右移一位
    }
    return res;
}

迭代实现方式中建议结合位运算,为什么?

位运算(&, >>)是处理器最基本的操作,通常只需要1个时钟周期
算术运算(%, /)通常需要3-5个时钟周期
在x86架构下测试,位运算版本相比传统版本快约30-40%

性能基准测试:

我们在不同环境下测试三种实现(单位:纳秒/操作):

在i7-11800H处理器上测试计算3¹⁰⁰⁰⁰⁰⁰的10000次迭代:

再多提一嘴,虽然两种实现方式在算法复杂度上相同(O(log n)),但位运算版本在实际运行中具有明显的性能优势。理解这种差异有助于我们写出更高效的底层代码,特别是在需要频繁执行幂运算的密码学、图形学等领域。

(3)快速幂的模运算应用

计算aⁿ mod m:

long long mod_pow(long long a, long long n, long long m) {
    long long result = 1;
    a %= m;  // 先取模以防a过大
    while (n > 0) {
        if (n & 1) result = (result * a) % m;
        a = (a * a) % m;
        n >>= 1;
    }
    return result;
}

以下两种方式为扩展(用的不多) :

(4)斐波那契数列快速计算

#include <iostream>
using namespace std;

typedef long long ll;

// 快速斐波那契计算
ll fib(ll n) {
    if(n == 0) return 0;
    
    ll a = 1, b = 1, c = 1, d = 0;
    ll ta, tb, tc;
    
    for(ll i = n-2; i > 0; i >>= 1) {
        if(i & 1) {
            ta = a; tb = b;
            a = a*ta + b*tc;
            b = ta*b + b*d;
        }
        ta = a; tb = b; tc = c;
        a = a*ta + b*tc;
        b = ta*b + b*d;
        c = b; d = ta;
    }
    return a;
}

int main() {
    ll n;
    cout << "输入n: ";
    cin >> n;
    cout << "F(" << n << ") = " << fib(n) << endl;
    return 0;
}

结合取模运算可以防止溢出问题:

#include <iostream>
using namespace std;

typedef long long ll;

// 带模数的斐波那契
ll fib(ll n, ll mod = 1e9+7) {
    if(n == 0) return 0;
    
    ll a = 1, b = 1, c = 1, d = 0;
    ll ta, tb, tc;
    
    for(ll i = n-2; i > 0; i >>= 1) {
        if(i & 1) {
            ta = a; tb = b;
            a = (a*ta + b*tc) % mod;
            b = (ta*b + b*d) % mod;
        }
        ta = a; tb = b; tc = c;
        a = (a*ta + b*tc) % mod;
        b = (ta*b + b*d) % mod;
        c = b; d = ta;
    }
    return a % mod;
}

int main() {
    ll n, mod;
    cout << "输入n和mod: ";
    cin >> n >> mod;
    cout << "F(" << n << ") % " << mod << " = " << fib(n, mod) << endl;
    return 0;
}

(5)矩阵快速幂

#include<bits/stdc++.h>
using namespace std;

typedef vector<vector<long long>> Matrix;

Matrix matrix_mult(const Matrix& A, const Matrix& B, long long mod = 0) {
    int n = A.size();
    Matrix result(n, vector<long long>(n, 0));
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < n; ++j) {
            for (int k = 0; k < n; ++k) {
                result[i][j] += A[i][k] * B[k][j];
                if (mod) result[i][j] %= mod;
            }
        }
    }
    return result;
}

Matrix matrix_pow(Matrix mat, long long power, long long mod = 0) {
    int n = mat.size();
    Matrix result(n, vector<long long>(n, 0));
    // 初始化为单位矩阵
    for (int i = 0; i < n; ++i) result[i][i] = 1;
    
    while (power > 0) {
        if (power & 1) result = matrix_mult(result, mat, mod);
        mat = matrix_mult(mat, mat, mod);
        power >>= 1;
    }
    return result;
}

4.扩展:密码学中的应用

在RSA等加密算法中,模幂运算尤为关键。我们引入蒙哥马利约减等优化技术:

// 预计算常数用于优化模运算
constexpr long long computeR(long long mod) {
    return (1LL << 64) % mod;
}

// 优化后的模幂运算
long long optimizedModPow(long long a, long long n, long long mod) {
    long long R = computeR(mod);
    long long a_bar = (a * R) % mod;
    long long res_bar = R % mod;
    
    while (n) {
        if (n & 1) res_bar = (res_bar * a_bar) % mod;
        a_bar = (a_bar * a_bar) % mod;
        n >>= 1;
    }
    return (res_bar * 1) % mod; // 乘以1的逆元
}

 好了过多的就不扩展了,快速幂的分享就到这里,如果对你有帮助的话还请点个赞支持一下,非常感谢,如果你对算法很感兴趣的话记得点个关注,我会持续更新实用算法。

更多推荐