学习记录:快速幂

学习记录 快速幂

快速幂的递归实现

假设要算\(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;
}
posted @ 2020-05-10 15:43  Salty_Fish  阅读(166)  评论(0编辑  收藏  举报