C语言 - 快速幂 - 迭代法+递归法 - 详细讲解

快速幂的作用:

解决 求 a ^ n 的问题 (n可以大于1e18), 如果用for循环的话,毫无疑问直接炸掉 …… 所以也就用了算法复杂度在 o(log n)的快速幂算法来解决此类问题。

快速幂递归法(基于二分思想):

那么既然要用到二分法那么怎么二分? 既然要递归那么如何递归?

第一个问题如何二分 ?

就以2 ^ 10 为例 , 我们可以以指数入手进行二分 , 10 是一个偶数 ,那么 二分一下就是 2^5 * 2^5 ,之后 2^5 中 5为奇数 那么就让 2^5 = 2 * 2^4 ,那么就有了2^4 ,那么对于 2^4 就又有了偶数的2 ^ 4进行二分。

初始 二分变化
2^10 2^5 * 2^5
2^5 2 * 2^4
2^4 2^2 * 2^2
2^2 2^1 * 2^1
2^1 2 * 2^0

由可以看出 要对指数的偶数和奇数进行区分从而进行拆分,这样可以大大的增大程序的效率,就把 o(n)的算法化成了o(log n)。

第二个问题如何进行递归 ?

其实很明了可以看出如果想要递归,一定也是以指数为参数进行,因为指数是变化而且牵扯到二分,递归的出口就是指数为零结束,我们可以把奇数和偶数进行 if 判断,如果指数为偶数那么就返回 fib(a , b/2 , m), (其中a为底数,b为指数,m为模)

递归法代码以及解析如下:

#include
using namespace std;
typedef long long ll;
ll binarypow(ll a,ll b,ll m) {
	if(b==0) return 1;//如果b为0,那么a^0=1
	//b为奇数,转换为b-1
	if(b%2==1) return a*binarypow(a,b-1,m)%m;//可以把if(b%2==1)该为if(b&1)这样会更快一点。
	else {//b为偶数,转换为b/2;
		ll mul=binarypow(a,b/2,m);
		return mul*mul%m;
	}
}
int main() {
	ll a,b,m;
	cin>>a>>b>>m;
	ll result=binarypow(a,b,m);
	cout<<result<<endl;
	return 0;
}

快速幂迭代法:

迭代法要用到一些二进制,以及位计算的思想,
就如2^13 我们依然以指数入手,我们可以把13化为二进制 就是 1 1 0 1 我们发现 1 出现在了 3 号位 , 2 号位 , 以及 0 号位
那么 13 = 2^3 + 2^2 + 2^1 = 8 + 4 + 1 ,那么划开不就是
2 ^ 13 = 2 ^ 8 * 2 ^ 4 + 2 ^ 1
如果把一个指数这样进行拆分那么不就可以对
那么问题 1 如何拆分?

问题:如何拆分 ?

我们可以用 if (b & 1) 判断指数的末尾是否为 1 (其中b 为指数 ,末尾为1 说明是奇数,末尾为0说明为偶数)如果为 1 那么就让一个变量乘以当前的 ,例如 变量 :int ans = 1; 让ans * =a(a为底数),然后每一次都要a^2 (移到下一位)。然后通过b>>=1把指数移到下一位判断下一位的末尾数是1 ,还是 0 ,然后就这样进行一步一步的拆分。
文字看不懂的话再加一个表来辅助理解。

b b & 1 ans a
1 a
1101 1 1*a a^2
110 0 a a^4
11 1 a * a^4 a^8
1 1 a^5 * a^8

迭代法代码以及解析如下:

#include
using namespace std;
typedef long long ll;
ll binarypow(ll a,ll b,ll m){
	ll ans=1;
	while(b>0){
		if(b&1){//如果b的二进制末尾为1 ,就相当于被选中了。
		//就如2^13 ==> 2^(13==>1101)==> 2^(1101) ==> 3 2 0 号为 1 那么被选中 ==> 2^13 = 2^8 *  2^4 * 2^1
			ans=ans*a%m;//令ans累积上a 
		}
		a=a*a%m;//令a平方 
		b>>=1;//将b的二进制右移一位即 
	}
	return ans;
}
int main(){
	ll a,b,m;
	cin>>a>>b>>m; 
	ll result=binarypow(a,b,m);
	cout<<result<<endl; 
	return 0;
} 

你可能感兴趣的:(C语言/C++,快速幂)