快速幂的理解及使用

快速幂

1.快速幂定义

wiki

快速幂也称为平方求幂(exponentiating by squaring)

快速幂时计算一个数的大正整数乘幂的一般方法(对多项式,矩阵也适用)

\[x^n = \begin{cases} x(x^2)^{\frac{n-1}{2}}, & \text {if $n$ is odd}\\ (x^2)^{\frac{n}{2}}, & \text {if $n$ is even} \end{cases} \]

平方法转换思想

将指数的位,二进制的位,来确定计算哪些幂

\[x = a * b ^n , x= a * (b^2)^{\frac{n}{2}} \]

每次把b进行平方,将n看作一个二进制的数,如果k位为1,则最后的结果需要乘上 b(2k)

b= 13(1101)

\[x = b^{13} , = b^1 * b^{4} * b^8 \]

代码实现

# a * b^n
def quickPower(a,b,n):
  x = a
  p = b
  while n >0:
    if(n & 1) == 1:
      x = x * p
    p = p * p   # p,p^2,p^4,p^8
    n = n >> 1
  return x
	
  • 上述对矩阵同样适用
  • 由于幂函数求解,数值会比较大,通常在每一步计算的时候都会进行取余操作
// b^n
int MOD = 10e9;
long long power(long long p,long long n){
  long long ans = 1;
  while(n > 0){
    if (n & 1 == 1) ans = (ans * p) % MOD;
    p = p * p % MOD;
    n >>=1;
  }
}

2.快速幂应用

计算大指数幂除以一个数的余数,在密码学中应用较多

LeetCode.372超级次方

// a^b % MOD
// b是以一个大数,保存在数组中
class Solution {
    private int MOD = 1337;
    public int superPow(int a, int[] b) {
      	return dfs(a,b,b.length -1);
    }
    private int dfs(int a,int[] b,int len){
        if(len == -1) return 1;
        return quickPow(dfs(a,b,len-1),10)  * quickPow(a,b[len]) % MOD;
    }
  	private int quickPow(int a ,int b){
    		int ans = 1;
        a %= MOD;
        while(b > 0){
            if( b & 1) ans = ans * a % MOD;
            a = a * a % MOD;
            b = b >> 1;
        }
        return ans;
    }
}

快速幂在动态规划中的应用

动态规划主要用来解决两种问题:

  • 1.优化问题
  • 2.组合计数问题

快速幂可以在组合计数问题中,对计算进行加速(时间复杂度从 O(n) -> O(logn))

[LeetCode.1411]

\[dp[i][0] = dp[i-1][0] * 3 + dp[i-1][1] * 2 , dp[i][1] = dp[i-1][0] * 2 + dp[i-1][1] * 2 \]

边界条件

\[dp[1][0] = dp[1][0] = 6 \]

    int numOfWays(int n) {
        constexpr int MOD = 1e9 + 7;
        
        vector<vector<long>> dp(n+1,vector<long>(2,6));
        for(int i = 2;i<=n;i++){
            dp[i][0] = (dp[i-1][0] * 3 + dp[i-1][1] *2) % MOD;
            dp[i][1] = (dp[i-1][0] * 2 + dp[i-1][1] *2) % MOD;
        }
        return (dp[n][0] + dp[n][1]) % MOD;
    }

转换为矩阵求解

\[(dp[i][0],dp[i][1]) = (dp[i-1][0] ,dp[i-1][1]) * \begin{matrix} 3&2\\ 2&2\\ \end{matrix} , (dp[n][0], dp[n][1] = (dp[1][0] , dp[1][1])* \begin{matrix} 3&2\\ 2&2\\ \end{matrix} ^{n-1} \]

class Solution {
public:
  int numOfWays(int n) {
    constexpr long kMod = 1e9 + 7;
    vector<vector<long>> ans{{6, 6}}; // 1x2
    vector<vector<long>> M{{3, 2},{2,2}}; // 2x2
    auto mul = [kMod](const vector<vector<long>>& A, 
                      const vector<vector<long>>& B) {
      const int m = A.size(); // m * n
      const int n = B.size(); // n * p
      const int p = B[0].size();
      vector<vector<long>> C(m, vector<long>(p));
      for (int i = 0; i < m; ++i)
        for (int j = 0; j < p; ++j)
          for (int k = 0; k < n; ++k)
            C[i][j] += (A[i][k] * B[k][j]) % kMod;
      return C;
    };
    --n;
    while (n) {      
      if (n & 1) ans = mul(ans, M); // ans = ans * M;
      M = mul(M, M); // M = M^2
      n >>= 1;
    }
    // ans = ans0 * M^(n-1)
    return (ans[0][0] + ans[0][1]) % kMod;
  }
};
不要用狭隘的眼光看待不了解的事物,自己没有涉及到的领域不要急于否定. 每天学习一点,努力过好平凡的生活.
原文地址:https://www.cnblogs.com/GeekDanny/p/15779513.html