1. 程式人生 > 其它 >快速冪的理解及使用

快速冪的理解及使用

快速冪

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(2

k)

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;
  }
};
不要用狹隘的眼光看待不瞭解的事物,自己沒有涉及到的領域不要急於否定. 每天學習一點,努力過好平凡的生活.