数论-详解快速幂

快速幂算法

一步一步优化

1.什么是快速幂算法

例题引入

A^B^ 的最后三位数表示的整数为?

正常求法

long long normalPower(long long base,long long power)
{
	long long result = 1;
	for(int i = 1;i <= power;i++)
	{
		result = result*base;
	}
		return result%M;
}
//T = O(n)	取决于power ,但这里会出现问题,数据会溢出 
//输出结果爆掉 
指数:在乘方a中,其中的a叫做底数,n叫做指数,结果叫幂。
______________________________________________________

f(x)=a^x , 随着x单位长度的递增,f(x)会呈“爆炸性”增长。
一张纸对折一次,厚度变成原来的2倍。再对折第二次,变为原来的2的2次方倍即4倍。
以此类推,假设纸的厚度为0.1mm,则对折24次以后,长度超过1千米;对折39次达55000千米,
超过地球赤道长度;对折42次达44万千米,超过地球至月球的距离;对折51次达22亿千米,
超过地球至太阳的距离;对折82次为51113光年,超过银河系半径的长度。

因此,如果题目让你求2的100次方,貌似我们程序设计语言中最大的long long类型也无法承载
这么大的数值,所以题目才不会要求你输出结果,因为结果可能会非常的大,大到没有任何类型
可以承载。所以我们会发现上面的结果为什么是0,因为已经发生溢出了 

然后我们就需要用到“取模”运算的运算法则: 
(1)(a + b) % p = (a % p + b % p) % p 

(2)(a - b) % p = (a % p - b % p) % p 

(3)(a * b) % p = (a % p * b % p) % p 

利用第三条,(a*b*c)%p == (a%p*b%p*c%p)%p

化简代码如下(解决爆精度问题)

long long normalPower(long long base,long long power)
{
	long long result = 1;
	for(int i = 1;i <= power;i++)
	{
		result *= base;
		result %= 1000;
	}
	return result%1000;
}
//T = O(n) 数据溢出解决了,但是还有问题在于时间复杂度,这里就还需要通过 n 转 log(n) 来实现
//输出时间为18s 对于 1000000000 次方

2.快速幂算法初步入门

所以就出现了下面这种方法化简计算步骤
3^10=3 * 3 * 3 * 3 * 3 * 3 * 3 * 3 * 3 * 3
//尽量想办法把指数变小来,这里的指数为10

3^10=(3*3)*(3*3)*(3*3)*(3*3)*(3*3)

3^10=(3*3)^5

3^10=9^5 

每次都将指数的大小减半,n->n/2,前提是 n 为奇数的时候
当 n 为偶数,也不太影响,将多的一位提出来乘 

代码如下

	long long fastPower(long long base,long long power)
	{
		long long result = 1;
		while(power > 0)
		{
			if(power%2 == 1)
			{
				result = result*base%1000;
			}
			power /= 2;
			base = (base*base)%1000;
		}
		return result;
	}

3.优化快速幂算法

power%2 == 1 可以使用 power&1 来替代,如果 p 为偶数,最后一位为 0 ,否则为 1,
所以就有 5&1 = 1,6&1 = 0
并且对于 power /= 2 来说,可以使用位运算实现减半操作 >> 1 右移一位,表示的就是变成原来的一半

代码如下

	long long fastPower(long long base, long long power) {
    long long result = 1;
    while (power > 0)
	{
        if (power & 1) //此处等价于if(power%2==1)
		{
            result = result * base % 1000;
        }
        power >>= 1; //此处等价于power=power/2
        base = (base * base) % 1000;
    }
    return result;
	}

所以说,所有的加减乘除运算均可使用位运算替代

简写代码如下

typedef long long ll
ll ksm(ll a,ll p)
{
	ll res = 1;
	while(p)
	{
		if(p&1) res = res*a%M;	//如果为奇数会走两次这里,1.p刚开始为奇数的时候。2.p == 1 的时候 
		a = a*a%M;
		p >>= 1;	//同 int 型的出发一样,3/2 == 1	。这里其实就是看二进制的最后一位是否为 1 来判断奇偶性,不管是不是奇都一分为二 
	}
	return res;
}
typedef long long ll;
ll ksm(ll a,ll p){ll res = 1;while(p){if(p&1){res = res*a%M;}p >>= 1;a = a*a%M;}return res;}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值