快速幂的理解及使用
快速幂
1.快速幂定义
快速幂也称为平方求幂(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.快速幂应用
计算大指数幂除以一个数的余数,在密码学中应用较多
// 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;
}
};
不要用狭隘的眼光看待不了解的事物,自己没有涉及到的领域不要急于否定.
每天学习一点,努力过好平凡的生活.