快速幂算法详解

目录

介绍

原理1

实现过程

原理2

取余运算

介绍

快速幂算法的目的就是让计算机很快地求出a^{b},暴力相乘的话,电脑要计算b次。用快速幂,计算次数在log_{2}b级别,很实用。

原理1

(1)如果将a自乘一次,就会变成a^{2}。再把a^{2}自乘一次就会变成a^{4}。然后是a^{8}…自乘n次的结果是a^{2n} 

(2)a^{x}\times a^{y}=a^{x+y}

(3)将b转化为二进制观看一下:比如b=\left ( 11 \right )_{10}就是\left ( 1011 \right )_{2}。从左到右,这些1分别代表十进制的8,2,1。可以说a^{11}=a^{8}\times a^{2}\times a^{1}

实现过程

假设我们拿到了a,并且b=11。想求a^{11},但是又不想乘11次,有点慢。

·以电脑视角稍稍观察一下b=11,二进制下是b=1011。

·制作一个base。现在base=a,表示的是a^{1}=a。待会base会变的。

·制作一个ans,初值为1,用来存放答案。

·循环一:看b(二进制)的最后一位是1吗? 是的。这代表a^{11}=a^{8}\times a^{2}\times a^{1}中的“\times a^{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努力上升,它通过自乘一次,使自己变成a^{2}

base *= base;

同时

b>>=1

它把(二进制的)自己每一位都往右移动了。原来的最后第二位,变成了最后第一位b=\left ( 101 \right )_{2}

·循环二,再看看b,最后一位还是1。这说明有“\times a^{2}”,ans∗=base。

·base 继续努力,通过base∗=base 让自己变成了a^{4}。然后b也右移一位b=\left ( 10 \right )_{2}

·循环三,可是b的最后一位不再是1了,说明不存在“\times a^{4}”.base自我升华,达到了a^{8}。且b>>=1。这一步中,答案没有增加,可是毕竟b>0,还有希望。

·循环四,b的最后一位是1,这说明“\times a^{8}”的存在。ans∗=base。由于b再右移一位就是0了,循环结束。

总的来说,如果b在二进制上的某一位是1,我们就把答案乘上对应的a^{2n}。完整代码如下:

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;
}

原理2

快速幂算法详解_第1张图片

这是2017年NOIP普及组的完善程序第1题,这里提示的思路和上面不一样。

思路为:若当前p为偶数,只需把x自乘,然后p/=2 ,即考虑下一层,下几层会帮我们乘上\left ( x^{2} \right )^{p/2}

若当前p为奇数,说明x^{p}=x\times \left ( x^{2} \right )^{p/2}中前面那个x存在,ans∗=x。然后继续考虑下一层,下几层会帮我们乘上\left ( x^{2} \right )^{p/2}。注意,这里的x不是指题目开始给出的x,而是当前层的x应有的值,这跟上面的base是一样的。

比如假设我们拿到了x=3,并且p=11。想求3^{11}

·第一层循环b=11,一个奇数。将3^{11}分解为3^{1}\times \left ( 3^{2} \right )^{5}来看。本层只需把ans∗=3^{1}。后面的到下一层再搞定。下几层的总目标是让ans∗=\left ( 3^{2} \right )^{5},也就是让ans∗=9^{5}。来到下一层的方法是x=3∗3=9且b=11/2=5。

·第二层循环几乎独立于第一层存在。b=5,一个奇数。将9^{5}分解为9^{1}\times \left ( 9^{2} \right )^{2}来看。本层只需把ans∗=9^{1}。后面的到下一层再搞定。下几层的总目标是让ans∗=\left ( 9^{2} \right )^{2},也就是让ans∗=\left ( 81 \right )^{2}。于是x=9∗9=81且b=5/2=2。

·第三层循环b=2,不是奇数,只把81^{2}当作\left ( 81^{2} \right )^{1}。下几层的总目标是让ans∗=\left ( 81^{2} \right )^{1}。于是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等效。

快速幂算法详解_第2张图片

取余运算

快速幂经常要结合取余运算。

取余运算有一些好用的性质,包括:

(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;
	}

 能保证这样下来最后的结果与“先乘到最后,再取余”的结果一样。

你可能感兴趣的:(快速幂算法专题,算法,c++)