快速幂与快速幂取模

快速幂

对大数时间复杂度的优化,具体操作是利用二进制操作

11的二进制是1011,11 = 2³×1 + 2²×0 + 2¹×1 + 2º×1,因此,我们将a¹¹转化为算 a(20)*a(21)*a(23) ,看出来快的多了吧原来算11次,现在算三次
& 运算还可以判断奇偶x&1== 0为偶,x&1== 1为奇

int ksm ( int a, int b)
{
    int ans = 1;
    int ans = 1, base = a;
    while( b!= 0)
    {
        if(b &1 != 0)//  &1 操作是取二进制的最末位
            ans *= base;
        base *= base;
        b >>= 1;// >>操作是二进制去掉最后一位
    }
    return ans;
}

以b==11为例,b=>1011,二进制从右向左算,但乘出来的顺序是 a(20) * a(21) * a(23),是从左向右的。我们不断的让base*=base目的即是累乘,以便随时对ans做出贡献。

typedef long long ll;
ll quick_pow(ll a, ll n)//a^n
{
    ll re = 1;
    while(n != 0)
    {
        if(n & 1)//判断n的最后一位是1还是0
            re *= a;
        a = (a * a);//将a平方,即把a^(2^i)变为a^(2^(i + 1))
        n >>= 1;//n右移一位,舍去n的最后一位
    }
    return re;//如果需要取模 可对re和a取模
}

快速幂取模

这一部分就跟数论关系很大了。取模也是数论问题中经常出现的。那么对于幂来取模,如果我们直接用模运算实际上是速度很慢的(因为试除法)。所以我们不妨在求快速幂的时候添加一些内容,从而得到结果。这个算法需要了解一下数论的一个定理:

(ab) mod c = ((a mod c)(b mod c)) mod c

那么根据上面的定理可以推导出另一个定理:

(a^b) mod c = (a mod c)^b mod c

具体的证明这里不再赘述,主要是看第二个定理,快速幂取模。我们可以在求快速幂的时候,通过对底数取模的方式,不断缩小底数的规模。那么我们在上面快速幂的基础上,添加取模,就可以完成整个操作。

int pow_mod(int a,int b,int c){
    int ans = 1;
    int base = a%c;
    while(b){
        if(b & 1) ans = (ans*base)%c;
        base = (base*base)%c;
        b >>= 1;
    }
    return ans;
}

如果能理解上面的快速幂算法,那么这个也会比较好理解了。定理中,底数是要有一次取模运算的。这里我们在给base赋值的时候就运行了一次。那么对于后面的一次取模,我们实际上利用了分配率,即:
   (ab) mod c = ((a mod c)(b mod c)) mod c
我们求幂的本质仍然是求积。所以每次我们对base或者ans进行运算的时候,都必须使用一次分配率,所以都要mod c。

猜你喜欢

转载自blog.csdn.net/king9666/article/details/89810788