快速幂

递归求解

快速幂实际是分治的思想,把大的次幂一步步除以二。例如求解x17x^{17},幂向下「递」的过程为:17->8->4->2->1,1即x本身,向上「归」即可得到结果。代码如下:

1
2
3
4
5
6
7
8
public double pow(double x, long p){
if(p == 0) return 1;
if(p == 1) return x;
double sub = pow(x, p/2);
double ret = sub * sub;
if(p % 2 == 1) ret *= x;
return ret;
}

迭代求解

1
2
3
4
举例x^77,77的二进制数每一位都有相应的权值
1 0 0 1 1 0 1
x^64 x^32 x^16 x^8 x^4 x^2 x^1
最终结果就是所有二进制位为1的权值之积:x^1 * x^4 * x^8 * x^64 = x^77

可以通过上面的例子直观理解,其实就是把幂次转换为二进制,从低位开始遍历,同时维护xpx^{p},其中pp为2的幂,如果某个二进制位为1,将其对应的xpx^p累乘到结果。代码如下:

1
2
3
4
5
6
7
8
9
public long pow(long x, int p){
long ret = 1;
while(p > 0){
if(p % 2 == 1) ret = ret * x % MOD;
x = x * x % MOD;
p >>= 1;
}
return ret;
}

这里涉及到了另一个小知识点为快速幂取模,分治运算时顺便取下模就好了。

由于本质是把幂次转换为二进制,快速幂的时间复杂度为O(logn)。

矩阵快速幂

在求解某个矩阵matmat的幂时同样可以采用快速幂,因为矩阵乘法与数值乘法有类似的幂次分解性质。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public long[][] pow(long[][] mat, int n){
long[][] ret = {{1, 0, 0, 0, 0, 0}};
while(n > 0){
if(n % 2 == 1) ret = multiply(ret, mat);
mat = multiply(mat, mat);
n /= 2;
}
return ret;
}

public long[][] multiply(long[][] a, long[][] b){
int m1 = a.length, n1 = a[0].length;
int m2 = b.length, n2 = b[1].length;
long[][] c = new long[m1][n2];
for(int i = 0;i < m1;++i){
for(int j = 0;j < n2;++j){
for(int k = 0;k < n1;++k){
c[i][j] = (c[i][j] + a[i][k] * b[k][j] % MOD) % MOD;
}
}
}
return c;
}

题目

50. Pow(x, n) - 力扣(LeetCode) 快速幂模板题

372. 超级次方 - 力扣(LeetCode) 快速幂取模

552. 学生出勤记录 II - 力扣(LeetCode) 用到了矩阵快速幂,但关键是得到需要幂运算的矩阵

__END__