快速幂
快速幂
一般的求幂方法#
初学编程时,学校一般都会出求x的n次幂的题目。而在做基础题的时候,也经常会用到math.h中的pow函数来求x的n次幂。但是,这些方法不一定能满足我们开发中所需效率。
//迭代法
int pow(int x, int n){
int ans = 1;
while(n--)
ans *= x;
return ans;
}
//递归法
int pow(int x, int n){
//base case
if(!n) return x;
return x*pow(x, n-1);
}
这种方法是初学时常用的解法,日常使用也是足够的,时间复杂度为O(N),而递归法则要用到O(N)的空间复杂度。
但有时,我们的底数和指数中有小数或指数有负数时,这种方法往往还需要再写多几行判断和扩展。那么我们就会用pow来求,往往就会用到精度更高的double类型变量,内部的实现远比上述复杂,所以仅仅在整数间运算,我们没必要用pow函数。
快速幂算法#
整数间的求幂可以用快速幂算法来把时间复杂度降到O(logN),快速幂算法不仅常见,后续很多算法也会用到。
接下来我们讨论这个算法的原理。
这是一个二分的分治思路,我们可以不断地把偶数情况一直拆解成相似的小问题,那么递归方程也显而易见了
//递归方法实现
//time complexity:O(logN)
//space comlexity:O(logN)
int quickPow(int x, int n){
if(!n) return 1;
else if(n & 1) return quickPow(a, n-1)*a; //此处位运算为判断奇数
else{
int temp = quickPow(a, n/2);
//这里必须用一个变量储存递归结果,如果在return里面调用两次递归,就不是分治了,时间复杂度还是线性阶
return temp*temp;
}
}
实际问题中,如果计算结果特别大,我们会对一个大素数进行取模,这时快速幂算法也需要取模,而且是步步取模。我们的素数较大,可能需要用到long long型变量来运算。
#define MOD 1000000007
long long quickPow(long long x, long long n){
if(!n) return 1;
else if(n & 1) return quickPow(x, n-1) % MOD;
else{
long long temp = quickPow(a, n/2) % MOD;
return temp*temp;
}
}
递归的代码简洁,又能非常贴切的描述上面推导的递归方程,效率也能比一般求幂方法高,但是众所周知递归是会占用额外的程序栈空间的,所以如果我们能把这个过程换成迭代来实现,效果会更好。
快速幂的迭代方法#
迭代方法中,我们就需要换一个角度去思考这种分治的思想。其实在上面的代码中,我们用了一个temp变量来储存了当前层递归的结果来达到分治,那么在迭代中,如果也能不停地调用前面的计算结果,减少运算次数,也就提高了效率。
我们知道,数值数据在计算机中以二进制储存,我们可以在推导中把指数看作二进制形式,我们以x的7次方为例
可以看出,我们只需要不断地把当前底数乘方,再让幂除二就可以实现快速幂算法了。当然,奇数情况下要先乘一个x。
int quickPow(int x, int n){
int ans = 1;
while(n){
if(n & 1)
ans *= x;
x *= x;
n >= 1; //右移一位,相当于除2
}
return ans;
}
虽然上述实现为整数,但其实只要x的数据类型支持乘法且满足结合律,快速幂的算法都是有效的。矩阵,高精度整数都可以照搬这个思路。
拓展:为什么要对大素数“1000000007”取模#
有时候,我们的算法得出结果会超过32位整型的范围,题目为了精度或是防止整数溢出,就需要对一个大数字进行取模。
那么为什么是1000000007呢?
因为这个数字是10位的最小质数,跟质数取模能最大程度避免冲突。
而对于int32位的最值,1000000007这个数字足够大,它的平方在int64的范围中也不会溢出。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)