快速幂取余算法总结详解

废话不多说, 直接步入正题。

现在oj网站的题或者竞赛的题,如果a的b次幂且b很大,那么题中大多会让你把结果对一个数取余也就是求模,例如a^b%c这种,当然如果是考高精度的题除外。

接下来我将提供一种常规算法和两种竞赛中主流的快幂算法。

首先我们设题目要求为a^b mod c

常规算法

这里我就不多作解释,直接码代码了

 

int mod(int a, int b, int c){
	int i, ans = 1;
	for (i = 0; i < b; i++)
		ans = (ans * a) % c;	//在这里对c取余是为了防止数据溢出
	return ans;
}

 

这个算法的时间复杂度为O(n),不是很高,但是一旦题目中b的取值很大且时间要求高的话,那么这个时间复杂度就危险了,接下来得靠我们的快幂算法来摆平。

 

快幂算法1

这里我们需要两个公式:

 

这两个公式都不难理解,自己可以验证一下,3^4 = 9^2。

有了这两个公式之后我们就可以考虑思路了。

我们就以b为偶数来举例。

a^b%c = ((a^2)^b/2)%c;

在这里我们假设b/2还是偶数,那么

((a^2)^b/2)%c = (((a^2)^2)^(b/2)/2)%c;到这里就可以了.

我相信稍微懂点算法的人都可以看出来这是一个递归的过程,如果不理解那么自己再找几个数试一试

理解之后就可以看这个快幂的代码了

 

int quickMod(int a, int b, int c)
{
	int ans = 1;
	while (b)
	{

		if (b % 2 == 1)
			ans = (ans * a) % c;
		b /= 2;
		a = (a * a) % c;
	}
	return ans;
}

这个算法的时间复杂度为O(logn),对于大多数oj网站或者竞赛题相信都可以AC过去。

 


接下来这种算法虽然思路和上面的完全不一样, 但是其思想核心和上面的算法是有着异曲同工之妙啊。

快幂算法2

我们先将b按2进制展开假设b = 10, 那么b的二进制为1010,也就是0*2^0+1*2^1+0*2^2+1*2^3 = 10;

所以 a^b = a^(0*2^0+1*2^1+0*2^2+1*2^3 ) = a^(2^1) * a(2^3);这种简单的转换在初中就学过了吧,相信大家都懂

所以a^b%c = a^(2^1) * a(2^3) % c =( a^(2^1) % c) * (a(2^3)%c)%c;

讲到这里就可以写代码了

 

int quickMod(int a, int b, int n)
{
	int ans = 1;
	while (b)
	{  
		if (b & 0x1)	//如果二进制位为1
			ans = ans * a % n;
		a = a * a % n;
		b >>= 1;	//b向又移1位
	}
	return ans;
}


写到这里就差不多该结束了,相信有心的读者可以发现这两种快幂思想的核心都是二分法,所以时间复杂度都为O(logn)。

 

虽然推倒过程完全不一样,但是到代码实现这个层面几乎是一模一样的,if(b % 2 == 1) 就相当于if(b & 0x1), b /= 2也就是b>>=1。

所以算法是多么优美又多么奇妙,希望大家可以在算法之路上不断前行,继续努力!

也欢迎关注我的公众号,从自身和身边经历聊一些职场话题,带你避坑

快速幂取余算法总结详解_第1张图片

你可能感兴趣的:(编程之路,快幂,算法,取余,求幂的快速方法)