快速幂

引入:求ab%c其中a、b数值很大,可能达到1018

基础知识:

模运算:

证明:

由上述可知ab%m = (a%m)b%m

快速幂:

对于任何一个整数的模幂运算
a^b%c
对于b我们可以拆成二进制的形式
b=b0+b1*2+b2*2^2+...+bn*2^n
这里我们的b0对应的是b二进制的第一位
那么我们的a^b运算就可以拆解成
a^b0*a^b1*2*...*a^(bn*2^n)
对于b来说,二进制位不是0就是1,那么对于bx为0的项我们的计算结果是1就不用考虑了,我们真正想要的其实是b的非0二进制位

那么假设除去了b的0的二进制位之后我们得到的式子是
a^(bx*2^x)*...*a(bn*2^n)
这里我们再应用我们一开始提到的公式,那么我们的a^b%c运算就可以转化为
(a^(bx*2^x)%c)*...*(a^(bn*2^n)%c)
这样的话,我们就很接近快速幂的本质了

以b=11为例,b=11=1011(2),所以a^b = a^(2^0) * a^(2^1) * a ^ (2^3);

下面给代码:

 1 ll pow(ll a, ll b, ll m)
 2 {
 3     ll ans = 1;
 4     a %= m;
 5     while(b)
 6     {
 7         if(b & 1)ans = (ans % m) * (a % m) % m;
 8         b /= 2;
 9         a = (a % m) * (a % m) % m;
10     }
11     ans %= m;
12     return ans;
13 }

用上面的例子来模拟代码运行

b&1 = 1成立;所以ans *= a;这里就相当于ans = a^(2^0)

a *= a此时a为a^(2^1)

b/=2变成了5

b&1 = 1成立,ans *= a,此时ans = a^(2^0) * a^(2^1)

a *= a此时a为a^(2^2)

然后b/=2变成了2

b&1 = 1不成立

a *= a此时a为a^(2^3)

然后b/=2变成了1

b&1 = 1成立 ans*= a 此时ans =  a^(2^0) * a^(2^1) * a^(2^3)

a *= a此时a为a^(2^4)

b/=2变成0,跳出循环

上述就是通过二进制的方式来操作快速幂。

 

当mod过大的时候,需要在快速幂的基础上加上快速加法,防止乘法溢出

计算a*b%m可以(a % m)*(b%m)%m

但是当m很大的时候,还是会溢出。可以用快速加法防止溢出

ll mul(ll a, ll b, ll m)
//求a*b%m
{
    ll ans = 0;
    a %= m;
    while(b)
    {
        if(b & 1)ans = (ans + a) % m;
        b /= 2;
        a = (a + a) % m;
    }
    return ans;
}
ll pow(ll a, ll b, ll m)
{
    ll ans = 1;
    a %= m;
    while(b)
    {
        if(b & 1)ans = mul(a, ans, m);
        b /= 2;
        a = mul(a, a, m);
    }
    ans %= m;
    return ans;
}

 

posted @ 2018-04-12 13:12  _努力努力再努力x  阅读(504)  评论(0编辑  收藏  举报