学习记录:快速幂

Posted salty-fish

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了学习记录:快速幂相关的知识,希望对你有一定的参考价值。

学习记录 快速幂

快速幂的递归实现

假设要算(7^9),如果采取普通计算,也就是(7*7*7*7*7*7*7*7*7),共需要8次运算。

运用二分的思想,先算(7^4),然后通过(7^4*7^4*7)来计算$7^9 $,这样就只需要(3+1+1=6)次计算,然而这样还不够彻底,(7^4)还可以通过分解成(7^2*7^2)的形式,这样递归下去,就得到了时间复杂度为(O(logn))的快速幂算法。

int Quickpow(int a,int n)
{
	if (n==0)
		return 1;
	else if (n%2==1)
		return Quickpow(a,n-1)*a;
	else{
		int temp=Quickpow(a,n/2);//必须先保存下来,否则会算两遍
		return temp*temp;
	}
}

做题的时候,幂的结果可能会非常大,需要对一个大数取余,这时将上面的函数改成long long,在运算的每一步都要取余结果也要取余啊!改进代码如下:

const int MOD=1e9+7;
typedef long long ll;
ll Quickpow(ll a,ll n)
{
	if (n==0)
		return 1;
	else if (n%2==1)
		return Quickpow(a,n-1)*a%MOD;
	else{
		ll temp=Quickpow(a,n/2)%MOD;
		return temp*temp%MOD;
	}
}//可以过洛谷P1226,就是结果也必须取余

非递归实现

非递归实现主要用了二进制的思想。

在上面的递归实现中不难发现是每次都是将结果分割为两半,这正好对应了二进制。而且众所周知,位运算是比乘法运算快的,运用非递归实现因此比递归实现快一点。

还是(7^9)的例子,(9)的二进制形式为(1001),意味着(7^{(1001)_2})可以拆分为(7^{(1000)_2}*7^{(1)_2}),以此类推,任何幂总可以拆分成(a^{2^b})相乘的形式,因此就有了思路。

  1. 总体思路就是从二进制最后一位开始算起,如果在这一位的二进制数为1,则说明需要加上这一块;若是0则跳过。然后到下一位。不过在此用位运算的右移实现。
  2. 计算(a^{2^b}),可以用底数自乘来实现,在每次运算中代表在这一位的(7^{2^{x}})是多少。
  3. 判断二进制的情况,需要用一些位运算的基本知识。

非递归计算(7^9)的过程:

临时变量 指数情况(二进制) 目前的运算结果
7((7^{2^0 })) 1001 1*7=7
49((7^{2^1})) 0100 7
2401((7^{2^2})) 0010 7
5764801((7^{2^3})) 0001 7*5764801=40353607

代码实现:

int Quickpow(int a,int n)
{
	int res=1;
	while (n){			//最终右移的结果为0
		if (n&1)		//指数二进制末尾为1
			res*=a;		//乘以当前的底数
		a*=a;			//底数自乘
		n>>=1;			//指数右移一位
	}
	return res;
}

以上是关于学习记录:快速幂的主要内容,如果未能解决你的问题,请参考以下文章

出错记录矩阵快速幂

快速幂&快速乘法

算法学习快速幂

快速幂:quickpow

AcWing 875. 快速幂

快速幂初步学习