算法竞赛—快速幂

简单快速幂

对于计算 a b a^b ab ( a , b ∈ N , b ≠ 0 a,b∈N,b\neq0 a,bN,b=0),最简单的做法莫过于运用正常的乘法进行实现,则需要令a自乘b-1次,显然该操作需要进行b次循环,记时间复杂度为 O ( n ) O(n) O(n)

int a,b;
for(int i=1;i<b;i++)
{
	a*=a;//直接在变量a上自乘 最后的结果仍然是变量a
}

当b较小时,(例如 b = 63 b=63 b=63)用这种方式,程序也不会花费太多时间;然而当b很大时(例如 b = 1 0 10 b=10^{10} b=1010),程序则要进行 1 0 10 10^{10} 1010次循环,而这种量级很难在1秒内完成,易导致程序TLE。因此,为了处理大量级的幕次运算,就需要引进“快速幂”算法加速幂次运算。
我们知道, 2 15 = 2 14 ∗ 2 1 = 2 7 ∗ 2 7 ∗ 2 = [ ( 2 6 ∗ 2 ) ∗ ( 2 6 ∗ 2 ) ] ∗ 2 = [ ( 2 3 ∗ 2 3 ∗ 2 ) ∗ ( 2 3 ∗ 2 3 ∗ 2 ) ] ∗ 2 = . . . . . 2^{15}=2^{14}*2^1=2^7*2^7*2=[(2^6*2)*(2^6*2)]*2=[(2^3*2^3*2)*(2^3*2^3*2)]*2=..... 215=21421=27272=[(262)(262)]2=[(23232)(23232)]2=.....
从上例可以看出,2n次幂可以分拆成2个n次幂之积,而2n+1次幂可以分拆成2n次幂和1次幂之积,而其中2个同次幂我们只需要取其一计算就够了,所以,在计算前式时,我们实际上只需要计算 2 14 、 2 7 、 2 3 、 2 2^{14}、2^7、2^3、2 21427232就可以了。对于 a b a^b ab我们稍加计算就可以得出,我们只需要进行 log ⁡ b \log{b} logb次运算就可以了,大大加快了幂次运算的速度,时间复杂度达到 O ( log ⁡ n ) O(\log n) O(logn)

//用递归函数实现
int quick_pow(int a,int b){
	if(b==1) return a;//最内层递归到一次方时则返回a
	if(b&1) return quick_pow(a,b-1)*a;//当幂次为奇数时拆成a与偶数幂之积
	int res=quick_pow(a,b>>1);//当幂次为偶数时拆成2个幂次之积 引入变量res是为了只计算一次
	return res*res;

快速幂取模

然而,在C++中,当数字过大时会出现溢出的情况,因此我们通常会遇到对大数取模的要求。
先介绍一个简单的数论知识:
a ∗ b m o d    n = ( ( a m o d    n ) ∗ ( b m o d    n ) ) m o d    n a*b \mod n=((a \mod n)*(b \mod n))\mod n abmodn=((amodn)(bmodn))modn
举个例子:
2 15 m o d    107 = ( 2 14 ∗ 2 ) m o d    107 = ( 2 14 m o d    107 ) ∗ ( 2 m o d    107 ) m o d    107 = . . . 2^{15}\mod 107=(2^{14}*2)\mod 107=(2^{14}\mod 107)*(2\mod107)\mod107=... 215mod107=(2142)mod107=(214mod107)(2mod107)mod107=...
因此我们就可以使用快速幂取模:

#define ll long long
ll quick_pow(ll a,ll b,ll n){
	return a%n;
	if(b&1) return quick_pow(a,b-1,n)*(a%n)%n;
	int res=quick(a,b>>1,n);
	return res*res%n;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值