[算法] 快速幂

下面的代码均为 C++ 代码!

1. 普通快速幂

  • 例题:50. Pow(x, n)

    • 解法:递归或者迭代(时间复杂度均为\(O(log\space n)\),递归空间复杂度为 \(O(log\space n)\),迭代空间复杂度为 \(O(1)\) );
  • 迭代算法详解:(参考了官方题解:Pow(x, n) - 方法二:快速幂+迭代

    我们的目标是求 \(x\)\(n\) 次方。任何一个整数都可以用2的幂次方的和表示,这里就不证明了(学计算机的应该不至于这点都不会),因此可以使用二进制表示/存储(说白了就是数的二进制),例如5的二进制是101。即任意一个整数 \(n\),有

    \[n=a_02^0+a_12^1+...+a_k2^k \\ \]

    其中 \(a\) 为系数,取值为 0或1。

    因此,

    \[\begin{align} x^n &= x^{(a_02^0+a_12^1+...+a_k2^k)} \\ &=x^{a_02^0}·x^{a_12^1}· \space ... \space ·x^{a_k2^k} \end{align} \]

    由于,

    \[\begin{align} x^{2^{(b+1)}}&=x^{(2^{b}·2^1)}=x^{(2·2^b)}=x^{(2^b+2^b)}=x^{2^b}·x^{2^b} \space \\ 即,\quad x^{2^{(b+1)}}&=x^{2^b}·x^{2^b}=[x^{2^b}]^2 \end{align} \]

    因此,可以从 \(x\)(即 \(x^{2^0}\))开始平方得到 \(x^{2^1}\)\(x^{2^1}\)再平方得到 \(x^{2^2}\),...,直到得到 \(x^{2^k}\) 。在平方的过程中我们只需要在n的相应的二进制位为1时将相应的累平方乘以前面的结果就可(例如 n 的 第c位 为1,则将 \(x^{2^c}\) 乘以 answer 就可,answer是快速幂的结果,详细看下面的代码),直至遍历完 \(n\) 的所有位!

    class Solution {
        double quickMul(double x, long long n) {
            double answer = 1.0;
            double squareX = x;     // x,即为x^(2^0)
            while (n > 0) {
                if (n % 2 != 0) {	// 最后一位为1,则说明该
                    answer *= squareX;
                }
                squareX *= squareX;	// 累平方
                n >>= 1;			// n右移一位,也可以将n除以2
            }
            return answer;
        }
    public:
        // 求x的n次方
        double myPow(double x, int n) {
            return n > 0 ? quickMul(x, n) : 1.0 / quickMul(x, -(long long)n);
        }
    };
    

2. 矩阵快速幂(了解)

  • 例题:斐波那契数列

  • 下面为求裴波那契数列的第n项的代码(详细看上面的链接):

    // 定义一个 Solution 类封装需要的函数
    class Solution {
        const int MOD = 1000000007;
    
        // 2*2矩阵(方阵)乘法
        vector<vector<long long>> matrixMul(vector<vector<long long>> &x, vector<vector<long long>> &y) {
            vector<vector<long long>> res(2, vector<long long>(2));
            for (int i = 0; i < 2; i++) {
                for (int j = 0; j < 2; j++) {
                    res[i][j] = (x[i][0] * y[0][j] + x[i][1] * y[1][j]) % MOD;
                }
            }
            return res;
        }
    
        // 矩阵快速幂
        vector<vector<long long>> fastExponentiation(vector<vector<long long>> &x, int n) {
            vector<vector<long long>> res = {{1, 0}, {0, 1}};  // 单位矩阵
            vector<vector<long long>> exponent = x;            // 2^0
            while (n) {
                if (n & 1) {
                    res = matrixMul(res, exponent);
                }
                exponent = matrixMul(exponent, exponent);
                n >>= 1;
            }
            return res;
        }
    
    public:
        int fib(int n) {
            vector<vector<long long>> x = {{1, 1}, {1, 0}};  // 定义矩阵M
            return n < 2 ? n : fastExponentiation(x, n - 1)[0][0];
        }
    };
    
posted @ 2023-02-09 23:00  小贼的自由  阅读(26)  评论(0编辑  收藏  举报