先上代码
public static long process(long a, long x, final long mod) {
return x == 1 ? a :
process(a * a % mod, x >> 1, mod) * ((x & 1) == 0 ? 1 : a) % mod;
}
没错,上面就是全部的代码了,就一行,是不是感觉很神奇。
1. 什么是快速幂取模?
刚开始接触的时候听名字感觉很高大上的东西,后来发现就是算 a n m o d c a^n \mod c anmodc的一个东西。只不过时间复杂度是 O ( l o g n ) O(logn) O(logn)级别的。比普通的 O ( n ) O(n) O(n)快点而已。
2. 算法核心是什么?
算法的核心思想很简单,就是我们平常用的东西。
比如算 2 8 2^8 28 = 2 ∗ 2 ∗ 2 ∗ 2... = 4 ∗ 4 ∗ 4 ∗ 4 = 16 ∗ 16 2 * 2 * 2 * 2 ...= 4 * 4 * 4 * 4 = 16 * 16 2∗2∗2∗2...=4∗4∗4∗4=16∗16。这个就是快速幂,也就是说如果算 2 8 2^8 28本来是需要算7次 2 ∗ 2 2 * 2 2∗2的,现在只需要算3次 4 ∗ 4 4 *4 4∗4或者一次 16 ∗ 16 16 * 16 16∗16就能解决了。也就是说 2 8 = ( 2 2 ) 8 / 2 = ( 4 ) 4 = ( 4 2 ) 4 / 2 = ( 16 ) 2 2^8 = (2^2)^{8/2} =(4)^4=(4^2)^{4/2}=(16)^2 28=(22)8/2=(4)4=(42)4/2=(16)2
3. 怎么描述这个过程呢?
采用递归的方式很容易表达这个过程。
q
u
i
c
k
P
o
w
(
2
,
8
)
=
q
u
i
c
k
P
o
w
(
2
2
,
4
/
2
)
=
q
u
i
c
k
P
o
w
(
4
,
4
)
quickPow(2, 8) = quickPow(2 ^ 2, 4 / 2) = quickPow(4, 4)
quickPow(2,8)=quickPow(22,4/2)=quickPow(4,4)
q
u
i
c
k
P
o
w
(
4
,
4
)
=
q
u
i
c
k
P
o
w
(
4
2
,
4
/
2
)
=
q
u
i
c
k
P
o
w
(
16
,
2
)
quickPow(4, 4) = quickPow(4 ^ 2, 4 / 2) = quickPow(16,2)
quickPow(4,4)=quickPow(42,4/2)=quickPow(16,2)
也就是说
q
u
i
c
k
P
o
w
(
a
,
b
)
=
q
u
i
c
k
P
o
w
(
a
2
,
b
/
2
)
quickPow(a, b) = quickPow(a^2, b / 2)
quickPow(a,b)=quickPow(a2,b/2)
4. 上面过程有什么问题吗?
没错,上面过程确实有问题。上面只考虑了b是2的整数倍的情况,对于
2
3
2^3
23
这种情况是不管用的。因为pow(2, 3) != pow(4, 1)。那么怎么改正呢?
不难发现,如果是奇数的话,那么a最多剩下一个。所以如果b是奇数的情况下,只要在多乘以一个a就可以解决了。也就是pow(2,3) = pow(2,2) * 2
也就是说,如果
b
m
o
d
2
=
=
1
b\mod 2 == 1
bmod2==1
p
o
w
(
a
,
b
)
=
p
o
w
(
a
2
,
b
/
2
)
∗
a
pow(a,b) = pow(a ^ 2, b / 2) * a
pow(a,b)=pow(a2,b/2)∗a
5. 取模的含义是啥呢?
当时一直以为快速幂取模,是关于取模的高阶操作,后来发现取模就是凑数用的。快速幂取模,重点是快速幂。取模纯属因为计算机表达不了这么大的数字而已。取模用到了一个性质 a ∗ b m o d c = ( a m o d c ) ∗ ( b m o d c ) m o d c a*b \mod c = (a \mod c) * (b \mod c) \mod c a∗bmodc=(amodc)∗(bmodc)modc
用到快速幂里面就是对每一个乘法加上一个取模,防止溢出就行了。
- b为奇数的情况下
q u i c k P o w ( a , b ) = q u i c k P o w ( a 2 m o d c , b / 2 ) ∗ a m o d c quickPow(a, b) = quickPow(a^2\mod c, b/2) * a \mod c quickPow(a,b)=quickPow(a2modc,b/2)∗amodc
- b为偶数的情况下
q u i c k P o w ( a , b ) = q u i c k P o w ( a 2 m o d c , b / 2 ) m o d c quickPow(a, b) = quickPow(a^2\mod c, b/2) \mod c quickPow(a,b)=quickPow(a2modc,b/2)modc
看到这就知道,最上面的那行代码是什么意思了吧。也就是用 O ( l o g n ) O(logn) O(logn)的时间复杂度求 a n m o d c a^n \mod c anmodc