快速幂模板 (求余)

相信童鞋们做题多了会发现,很多题都是要求次幂的。如果数据比较小,用int 或者long long 的暴力也是可以出结果的,但很多题就不尽如人意了,数据往往非常大,long long 可能都存不下,甚至还会爆内存,导致数据出错。

这时候就需要更快更省内存的算法——快速幂

快速幂快速幂,就是快!想象一下,求2^16,是2*2*2*…乘16个2快,还是((2*2*2*2) * (2*2*2*2)) * ((2*2*2*2) * (2*2*2*2)) 再算 (2*2*2*2) * (2*2*2*2). . .比较快。所以,快速幂的原理来了:求a^n,只要求a^(n/2) * a^(n/2)即可,以此类推,倒着算,当然,这是n为偶数的情况。n为奇数时只要ans*a即可

int sq(ll a,ll n)
{
	if(n==1)
		return a; 
	else if(n%2==1)
		return (ll)pow(sq(a,n/2),2)*a;
	else
		return (ll)pow(sq(a,n/2),2);
}

这里判断次数是否为奇数,有多种方法(暂时我只知道两种):

① n%2(==1)   ② n&1(==1) 因为&是与符号,在计算机中存储都为二进制数,只有n的尾数为1(即n为奇数)时,1&1=1,if条件才成立

 

有时次幂结果太大的,题上会要求取余,那么取余应该怎么做呢?

离散数学讲到:积的取余等于取余的积的取余。

举个例子:(a * b)%mod = (a%mod * b%mod)%mod

在次幂也同样适用,比如:2^3%mod = ((2%mod * 2%mod)%mod * 2%mod )%mod

看起来很复杂,但是写成算法就还好,注意不要少了取余的个数,少取一次就误差一点,少取n多次。。嗯。。。

int sq(ll a,ll n)
{
	if(n==1)
		return a%mod; 
	else if(n&1)
		return ((ll)pow(sq(a%mod,n/2)%mod,2))%mod*(a%mod)%mod;
	else
		return ((ll)pow(sq(a%mod,n/2)%mod,2))%mod;
}

还有一种稍微优化一点的算法,省去了pow函数

ll sq(ll a,ll n)
{
	if(n==1)
		return a;
	ll ans = sq(a,n/2)%mod;
	if(n&1)
		return (ans* ans)%mod * (a%mod) %mod;
	else
		return ans * ans % mod;
}

上面的方法是递归,还有一种迭代的写法

ll quick_pow(ll a, ll n) 
{
    ll ans=1;
    while(n)
    {
        if(n&1)
        {
            ans=(ans*a)%mod;
            n--;
        }
        n/=2;
        a=a*a%mod;
    }
    return ans;
}

 

点赞