快速幂
1.快速幂定义
快速幂也称为平方求幂(exponentiating by squaring)
快速幂时计算一个数的大正整数乘幂的一般方法(对多项式,矩阵也适用)
x n = { x ( x 2 ) n − 1 2 , if n is odd ( x 2 ) n 2 , if n is even 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} xn={x(x2)2n−1,(x2)2n,if n is oddif n is even
平方法转换思想:
将指数的位,二进制的位,来确定计算哪些幂
$$
x = a * b ^n
\newline
x= a * (b2){\frac{n}{2}}
$$
每次把b进行平方,将n看作一个二进制的数,如果k位为1,则最后的结果需要乘上 b(2k)
b= 13(1101)
x
=
b
13
=
b
1
∗
b
4
∗
b
8
x = b^{13} \newline = b^1 * b^{4} * b^8
x=b13=b1∗b4∗b8
代码实现:
# 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]
d
p
[
i
]
[
0
]
=
d
p
[
i
−
1
]
[
0
]
∗
3
+
d
p
[
i
−
1
]
[
1
]
∗
2
d
p
[
i
]
[
1
]
=
d
p
[
i
−
1
]
[
0
]
∗
2
+
d
p
[
i
−
1
]
[
1
]
∗
2
dp[i][0] = dp[i-1][0] * 3 + dp[i-1][1] * 2 \newline dp[i][1] = dp[i-1][0] * 2 + dp[i-1][1] * 2
dp[i][0]=dp[i−1][0]∗3+dp[i−1][1]∗2dp[i][1]=dp[i−1][0]∗2+dp[i−1][1]∗2
边界条件
d
p
[
1
]
[
0
]
=
d
p
[
1
]
[
0
]
=
6
dp[1][0] = dp[1][0] = 6
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}
\newline
(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;
}
};