前言
本人第一次写博客,若有不当请见谅。
说明
最近在网上看了好几篇博客,也没看明白关于快速幂这个算法的时间复杂度是怎么优化的,这让数学差的人使劲挠头,后面又看了几篇快速幂的代码和思路,终于完全搞明白快速幂到底是个什么算法,下面写的是我个人对快速幂的看法和见解,关于原理的解释如果有错误请一定要评论我改正!
一般的求幂解法
基本思想就是乘以n次的a
int fun(int n,int a)//代表为求a的n次幂
{
int sum=1;
while(n)
{
sum*=a;
n--;
}
return sum;
}
可以看到该基本思想的时间复杂度为O(n),这样如果n的数值大一些,在很多的程序里面就会时间超限,所以在这里我们就需要用到快速幂算法,时间复杂度即为O(logn),n是幂,大大降低了时间复杂度。
快速幂解法(循环)
先说一下快速幂的循环解法:
关于快速幂,博主的主要理解就是分开相乘,即为an=(a2)^n/2
比如列举一个简单的列子:
———2的9次方
因为9/2等于4,因为c语言的向下取整会失去一次幂所以
当(此列子9为n)n为不是2的倍数时就用最终结果表示变量sum将他乘起来。
n的次方就相当于an=a*an-1,而a的一次方已经被sum乘起来所以n直接减去1就可以了。
此时就可以将a的值变为a2,n的值变为n/2;即分开相乘,因为是向下取整,所以上面的
n-1可以省略掉。
不多说了,直接上代码加注释,希望各位读者结合注释和上面的解释阅读和理解。
int fun1(int n,int a)//代表为求a的n次幂
{
int i,sum=1;//sum代表a的n次幂,所有0次幂都是1,所以这里初值为1
while(n)//当n等于0的时候结束循环
{
if(n%2!=0)sum=sum*a;
//当n为单数时候就让sum和a相乘一次,就相当于a^n=a*a^(n-1)
//n不需要减去1是因为int类型是向下取整
a=a*a;//a^n=(a^2)^(n/2),a此时就变为a^2
n/=2;//n此时就变为n/2
}
return sum;//求完后返回sum
}
快速幂解法(递归)
关于快速幂的递归写法是基于上面循环的思想改为递归,和上面思想基本一致。
首先要有递归结束条件,如果所求的n次幂为0次幂就直接返回1即可,因为所有数的0次幂都是1。
然后再后再考虑不是0的情况
如果n不是2的倍数也就是让此时的a和上次递归的返回值相乘这里的n需要减去1如果不减去1他
就会一直不是2的倍数,一直递归,死递归了。如果是2的倍数就进行下一次递归,传进去的值
就为a2和n/2。
此时也需要一个结束递归的条件,就是当n等于1的时候,就已经将n次幂的值都存进了a里面,直接返回即可。
int fun2(int n,int a)//代表为求a的n次幂
{
if(n==0)return 1;//如果n是0,就是任何数的0次幂都是1
if(n==1)return a;//当n为1的时候就要结束递归了,返回a
else
{
if(n%2==1)return fun2(n-1,a)*a;
//此时也是当n为单数时候就让上次的返回值和a相乘一次
return fun2(n/2,a*a);//此时a传进去下次递归为a^2,n为n/2
}
}
快速幂取模解法
基于以上的快速幂循环和递归的解法里面将所有赋值a和sum的时候取模一个需要被取模的值即
可,此处假设需要取模的值为123。
循环代码为:
int fun1(int n,int a)//代表为求a的n次幂
{
int i,sum=1;
while(n)
{
if(n%2!=0)sum=sum*a%123;
a=a*a%123;
n/=2;
}
return sum;
}
递归代码为:
int fun2(int n,int a)
{
if(n==0)return 1;
if(n==1)return a%123;
else
{
if(n%2==1)return fun2(n-1,a)*a%123;
return fun2(n/2,a*a%123);
}
}
此处取模是根据此公式:(ab)%c=((a%c)(b%c))%c 得来的,因为看快速幂的原理还是乘积,所以要在每个乘积的值后面取模。