快速幂算法
背景
- 计算幂指数时,如计算 2 10 2^{10} 210 ,可以2乘以10次,这样的话复杂度高。
- 也可以先计算 2 2 2^2 22,然后中间结果再平方 2 2 2 2^{2^{2}} 222 ,即先算2次方,然后再直接算4次方,然后算8次方,每次直接翻倍。这样的话复杂度降低了很多。这种方法叫做快速幂指数算法。
- 在计算 2 100 2^{100} 2100 时,普通算法需要计算100次乘法,而快速幂指数计算6次就到了64次方,一共也只需要计算9次。
方法
- 下面介绍快速幂指数方法。
- 首先我们先看 2 10 2^{10} 210,结果我们都知道等于1024。
- 在计算时,过程如下:
- 1、 取中间变量t(temp),结果变量r(result),指数p(power),底数b(base)。
- 2、初始化,b = 2;p = 10; t = b 1 t = b ^ 1 t=b1,r = 1;
- 3、注意到,p = 10的二进制为 p = 1010(bit),1010(bit) = 1000(bit) + 0010(bit)。
- 4、因此, 2 1010 ( b i t ) = 2 1000 ( b i t ) × 2 0010 ( b i t ) 2 ^ {1010(bit)} = 2 ^ {1000(bit)} \times 2 ^ {0010(bit)} 21010(bit)=21000(bit)×20010(bit) 。即 2 10 = 2 8 × 2 2 2^{10} = 2^8 \times 2^2 210=28×22。包括2次方项和8次方项。
- 5、我们进行翻倍计算。目前
t
=
b
1
t = b ^ 1
t=b1 ,令
t *= t
,则 t = b 2 t = b ^ 2 t=b2 。由于结果包含2次方项,因此r *= t
,则 r = b 2 r = b ^ 2 r=b2 。 - 7、继续翻倍,令
t *= t
,则 t = b 4 t = b ^ 4 t=b4 。 - 8、继续翻倍,令
t *= t
,则 t = b 8 t = b ^ 8 t=b8 。由于结果包含8次方项,因此r *= t
,则 r = b 10 r = b ^ {10} r=b10 。
- 以上过程,写成代码如下:
#include <iostream> using namespace std; int QuickPower(int b, int p) { int t = b; int r = 1; while(p) { if(p & 1) { r *= t; p -= 1; } else { t *= t; p >>= 1; } } return r; } int main() { cout << QuickPower(2,10); return 0; }
- 以上代码中,
if(p & 1)
作用是用来检测结果中是否包含改2次方项,如果包含,则把其乘以到结果中。
余数定理
- 在计算2的100次方时,结果会溢出。
- 因此经常要求结果按多少多少取余数。余数定理如下:(a * b)%c = ((a%c) * (b%c)) % c;
- 相乘的结果,等于各数余数的结果相乘再取余。
- 因此上面代码可以改为每次相乘后都取余数,假设按1000取余:
#include <iostream> using namespace std; int QuickPower(int b, int p) { int t = b; int r = 1; while(p) { if(p & 1) { r *= t; r %= 1000; p -= 1; } else { t *= t; t %= 1000; p >>= 1; } } return r; } int main() { cout << QuickPower(2,100); return 0; }