快速幂

求x^m 一般方法是 xm = x * xm-1,这样需要做m次乘法,未免过慢。

加速方法有两种。

1.基于当m为偶数时, xm = (x2)^(m/2) ;当m为奇数时, xm = x * xm-1。显然当m为偶数时m会减半,当m为奇数时,下次就是偶数。m可以很快收敛到0.(^表示幂)

2.将m看成二进制串mkmk-1…m1m0,那么xm = xm02^0 + m12^1 + … + mk2^k = xm02^0 xm12^1 xmk*2^k. mi为0或1,假设平均有一半mi为1,即k个,那么总共才只需要做(k+(k/2))次乘法。

下面给出代码。第一个方法是加速方法1,第二个方法是加速方法1的迭代形式,第三个方法是加速方法2。

在网上看到有人将 2 或 /2,改为移位运算,就说效率更高。这其实是扯谈。连我们都知道移位运算效率高,2 和 /2 就是一个相当于移位运算的操作,做编译器的人会不知道?即使你写成 *2 或 /2,编译器也会帮你优化为移位运算。不相信的同学可以用C语言测试一下,只需将代码编译成汇编代码看看是否一样。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
public class pow{
public static int fastExp(int x, int m){
if(m == 0)return 1;
if(m % 2 == 0){
//x^m = (x^2)^(m/2)
return fastExp(x*x, m/2);
}
else{
//x^m = x * x(m-1)
return x * fastExp(x, m-1);
}
}
public static int fastExp_iter(int x, int m){
int result = 1;
while(m != 0){
if(m % 2 == 0){
x *= x;
m /= 2;
}
else{
result *= x;
m --;
}
}
return result;
}
public static int fastExpBin(int x, int m){
//x^m = x^(m0 * 2^0) * x^(m1 * 2^1) * x^(m2 * 2^2) * ... * x^(mk * 2^k)
int result = 1;
while(m != 0){
if((m&1) == 1){
//m0 = 1
result *= x;
}
//x对应每一位mi
x *= x;
m >>= 1;
}
return result;
}
public static void main(String[] args){
for (int i = 0; i < 10; i ++)
System.out.print(fastExp(2, i) + " ");
System.out.println();

for (int i = 0; i < 10; i ++)
System.out.print(fastExp_iter(2, i) + " ");
System.out.println();

for (int i = 0; i < 10; i ++)
System.out.print(fastExpBin(2, i) + " ");
System.out.println();
}
}

快速幂取模

与快速幂类似

只是在每次运算的时候要作mod m运算,利用的是模运算规则 (a b) mod m = ((a mod m) (b mod m)) mod m.

用快速幂取模的方法比直接求幂再取模的方法要快,因为将乘数限制在一定的范围。