所谓的快速幂,实际上是快速幂取模的缩写
首先,最基本的办法是:
int ans = 1;
for(int i=1;i<=b;i++){
ans =ans*a;
}
ans =ans%c; //ans是对answer的缩写
但是如果a很大,那么a^b的结果就容易非常大,所以在求之前可以先对a做一个变化 如下:
int ans = 1;
a =a%c; //加上这一句
for(int i=1;i<=b;i++){
ans =ans*a;
}
ans =ans%c;
加上那一句之后,求出来的答案是不会变得,,可以举个数字体味一下哦!
按照上面的思路,我们还可以在别的地方再取一次余哦,让过程中的数字尽可能地小:
int ans = 1;
a =a%c; //加上这一句
for(int i=1;i<=b;i++){
ans =(ans*a)%c; //这里再次取了一次余
}
ans =ans%c;
当当当当~~~~~经过上面,我们终于可以用快速幂来求解了:
首先,快速幂的理论基础:
快速幂的过程是快速求出a^b的过程,算到最后ans还是要%c的:
快速求出a^b:
1、当b为偶数时,a^b可以转化为(a^2)的(b/2)次方
2、当b为奇数时,a^b可以转化为(a^2)的(b/2)次方 再乘 a //比偶数多一个乘a,可以放到最前面乘上
证明巴拉巴拉的我就不证了,可以带个数字体会一下这个过程哦!
看代码:
int ans = 1;
a =a%c; // a在此时%c是为了简化数字
if(b%2==1) ans = (ans*a)%c; //如果是基数,要求多一步,可以提前算到ans中 ,这里%c也是为了简化数字
k=(a*a)%c; //取a^2 //k在此时%c是为了简化数字
for(int i=1;i<=b/2;i++){
ans =(ans*k)%c;
}
ans =ans %c; //在求出a^b的最后不要忘记%c哦
我们可以看到,我们把时间复杂度变成了O(b/2).当然,这样子治标不治本。但我们可以看到,当我们令k = (a * a) mod c时,状态已经发生了变化,我们把最终(a^b)%c转化成了【(a^2)%c的b/2】%c,所以我们发现这个过程是可以迭代下去的。当然,对于奇数的情形会多出一项乘a的过程 ,这里像上面一样加在开始的地方ans = (ans * a) % c;就可以了。形如上式的迭代下去后,当b=0时,所有的因子都已经相乘,算法结束。于是便可以在O(log b)的时间内完成了。于是,有了最终的算法:快速幂算法。
int ans =1;
a=a%c;
while(b>0){
if(b%2==1) ans = (ans*a)%c;
b = b/2;
a = (a*a)%c;
} //这里我不太懂哎,,最后为什么没有在%c 但是既然是一个简化的过程,过程中应该已经省的很多了吧
把上面的代码结构化,也就是写成函数
int PowerMod(int a,int b,int c){
int ans =1;
a = a%c;
while(b>0){
if(b%2==1) ans = (ans*a)%c;
b = b/2;
a = (a*a)%c;
}
return ans;
}
本算法的时间复杂度为O(logb),能在几乎所有的程序设计(竞赛)过程中通过,是目前最常用的算法之一。
扩展:
有关于快速幂的算法的推导,还可以从另一个角度来想。
=? 求解这个问题,我们也可以从进制转换来考虑:
将10进制的b转化成2进制的表达式:
那么,实际上,.
所以
注意此处的要么为0,要么为1,如果某一项,那么这一项就是1,这个对应了上面算法过程中b是偶数的情况,为1对应了b是奇数的情况[不要搞反了,读者自己好好分析,可以联系10进制转2进制的方法],我们从依次乘到。对于每一项的计算,计算后一项的结果时用前一项的结果的平方取余。对于要求的结果而言,为时ans不用把它乘起来,[因为这一项值为1],为1项时要乘以此项再取余。这个算法和上面的算法在本质上是一样的,读者可以自行分析,这里我说不多说了,希望本文有助于读者掌握快速幂算法的知识点,当然,要真正的掌握,不多练习是不行的。