目录
介绍
原理1
实现过程
原理2
取余运算
快速幂算法的目的就是让计算机很快地求出,暴力相乘的话,电脑要计算b次。用快速幂,计算次数在级别,很实用。
(1)如果将a自乘一次,就会变成。再把自乘一次就会变成。然后是…自乘n次的结果是
(2)
(3)将b转化为二进制观看一下:比如b=就是。从左到右,这些1分别代表十进制的8,2,1。可以说。
假设我们拿到了a,并且b=11。想求,但是又不想乘11次,有点慢。
·以电脑视角稍稍观察一下b=11,二进制下是b=1011。
·制作一个base。现在base=a,表示的是。待会base会变的。
·制作一个ans,初值为1,用来存放答案。
·循环一:看b(二进制)的最后一位是1吗? 是的。这代表中的“”存在。所以ans∗=base。
if(b & 1)
ans *= base;
/*关于 b & 1:
x & y 是二进制 x 和 y 的每一位分别进行“与运算”的结果。
与运算,即两者都为 1 时才会返回 1,否则返回 0。
那么 b & 1
二进制
b = 1011
1 = 0001
b&1 = 0001
因为 1(二进制)的前面几位全部都是 0,
所以只有 b 二进制最后一位是 1 时,b & 1 才会返回 1。
挺巧妙的,并且很快。)*/
·然后base努力上升,它通过自乘一次,使自己变成。
base *= base;
同时
b>>=1
它把(二进制的)自己每一位都往右移动了。原来的最后第二位,变成了最后第一位b=。
·循环二,再看看b,最后一位还是1。这说明有“”,ans∗=base。
·base 继续努力,通过base∗=base 让自己变成了。然后b也右移一位b=。
·循环三,可是b的最后一位不再是1了,说明不存在“”.base自我升华,达到了。且b>>=1。这一步中,答案没有增加,可是毕竟b>0,还有希望。
·循环四,b的最后一位是1,这说明“”的存在。ans∗=base。由于b再右移一位就是0了,循环结束。
总的来说,如果b在二进制上的某一位是1,我们就把答案乘上对应的。完整代码如下:
int quickPower(int a, int b)//是求a的b次方
{
int ans = 1, base = a;//ans为答案,base为a^(2^n)
while(b > 0)//b是一个变化的二进制数,如果还没有用完
{
if(b & 1)//&是位运算,b&1表示b在二进制下最后一位是不是1,如果是:
ans *= base;//把ans乘上对应的a^(2^n)
base *= base;//base自乘,由a^(2^n)变成a^(2^(n+1))
b >>= 1;//位运算,b右移一位,如101变成10(把最右边的1移掉了),10010变成1001。现在b在二进制下最后一位是刚刚的倒数第二位。
}
return ans;
}
这是2017年NOIP普及组的完善程序第1题,这里提示的思路和上面不一样。
思路为:若当前p为偶数,只需把x自乘,然后p/=2 ,即考虑下一层,下几层会帮我们乘上
若当前p为奇数,说明中前面那个x存在,ans∗=x。然后继续考虑下一层,下几层会帮我们乘上。注意,这里的x不是指题目开始给出的x,而是当前层的x应有的值,这跟上面的base是一样的。
比如假设我们拿到了x=3,并且p=11。想求。
·第一层循环b=11,一个奇数。将分解为来看。本层只需把ans∗=。后面的到下一层再搞定。下几层的总目标是让ans∗=,也就是让ans∗=。来到下一层的方法是x=3∗3=9且b=11/2=5。
·第二层循环几乎独立于第一层存在。b=5,一个奇数。将分解为来看。本层只需把ans∗=。后面的到下一层再搞定。下几层的总目标是让ans∗=,也就是让ans∗=。于是x=9∗9=81且b=5/2=2。
·第三层循环b=2,不是奇数,只把当作。下几层的总目标是让ans∗=。于是x=81∗81=6561,b=2/2=1。
·第四层循环,b=1,是奇数。这时候已经不用看成什么分解了,ans∗=6561就可完成总目标。b/2 为 0,结束循环。
代码和上面一样。因为b&1与b mod 2==1等效。b/=2与b>>=1等效。
快速幂经常要结合取余运算。
取余运算有一些好用的性质,包括:
(A+B) mod b=(A mod b+B mod b) mod b
(A×B) mod b=((A mod b)×(B mod b)) mod b
于是快速幂过程中可以
while(b > 0)
{
if(b & 1)
{
ans *= base;
ans %= m;
}
base *= base;
base %= m;
b >>= 1;
}
能保证这样下来最后的结果与“先乘到最后,再取余”的结果一样。