快速幂算法及其C++代码

1 快速幂算法

问题1:对于x^{16},你是会选择使用x^{8}*x^{8}来计算,还是使用x^{15}*x来计算?

回答:x^{8}*x^{8}。显然,计算x^{8}要比计算x^{15}的代价更低。

        同理,任何幂次计算都可以使用上述的二分的方法,使得总的计算代价更低,即

x^{n}=x^{n/2}*x^{n/2}=x^{n/4}*x^{n/4}*x^{n/4}*x^{n/4}=...

        但是,上述的式子只是一个理想状态,即n能够被2以及2的更高次幂整除,也就是说n所有的二进制位上都为1。如果遇到其他的n,例如n=133,就不能简洁地使用上述的方法进行计算。

问题2:如何能够简洁地计算所有的n次幂呢?

回答:使用二进制。

        上面的问题其实也可以转换为,如何使用一个更通用的方法,将n拆成一系列的数的组合。回想一下,给定一个二进制,如何转换为十进制?例如给定二进制1011,转换成十进制如下所示(括号的下标代表进制数)

(1011)_{2} = 1*2^{0} + 1*2^{1}+0*2^{2}+1*2^{3} = (11)_{10}

        那么如果要计算x^{11},我们可以根据二进制,将其转换为

\begin{aligned} x^{11} &= x^{1*2^{0} + 1*2^{1}+0*2^{2}+1*2^{3}} \\ &=x^{1*2^{0}}*x^{1*2^{1}}*x^{0*2^{2}}*x^{1*2^{3}} \\ &=x^{1}*x^{2}*x^{0*4}*x^{8} \end{aligned}

        让我们观察上式的结果,可以发现展开后的四项,除了第一项x,其余的每一项都是前一项的平方,且每一项都对应二进制的一位。

        那么,我们就可以从低位到高位,遍历幂的二进制的每一位,只有当该位上是1时,将结果乘上相应的项,并使用一个变量来记录该项,每遍历一位,变量更新为自身的平方。这就是快速幂算法。伪代码如下所示

给定:底数x和幂y
初始化:res=1
while (未遍历完y的所有二进制位)
    if (当前遍历的二进制位是否为1) 
        rse = res * x
    x = x * x
    遍历下一位
return res

2 C++代码

快速幂算法(要注意这里面还有一个取模运算,防止指数运算溢出,模数取较大的素数即可):

#define mod 1000000007  // 选择一个较大的素数
int fast_pow(int x, int y) {
    int res = 1;  // 结果初始化为1,如果是矩阵的快速幂,那就初始化为单位矩阵
    while (y) {  // 判断是否遍历完
        if (y & 1) {  // 如果幂次当前位上是1
            res = (res * x) % mod;  // 如果是矩阵的快速幂,需要自己实现一个两个矩阵相乘的函数
        }
        x = (x * x) % mod;  // 将x更新为自己的平方,即x^{n} = x^{2*n},如果是矩阵的快速幂,这里同样使用矩阵相乘的函数
        y >>= 1;  // 下一次遍历下一位
    }
    return res;
}

方阵的快速幂算法:

// 计算两个方阵的乘法
vector<vector<int>> dot(vector<vector<int>>& a, vector<vector<int>>& b) {
    vector<vector<int>> res(m, vector<int>(m, 0));
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < m; j++) {
            for (int k = 0; k < m; k++) {
                res[i][j] += a[i][k] * b[k][j];
            }
        }
    }
    return res;
}
// 方阵的快速幂
vector<vector<int>> fast_pow_matrix(vector<vector<int>>& matrix, int y) {
    int m = matrix.size();
    // 初始化为单位矩阵
    vector<vector<int>> res(m, vector<int>(m, 0));
    for (int i = 0; i < m; i++) {
        res[i][i] = 1;
    }
    // 逻辑和数的快速幂算法一致
    while (y) {
        if (y & 1) {
            res = dot(res, matrix);
        }
        matrix = dot(matrix, matrix);
        y >>= 1;
    }
    return res;
}

  • 6
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值