快速幂+大数乘法取模

快速幂:

<math.h> 中自带的 pow 函数在调用时需要一系列类型转换;数值并不是严格精确,存在误差;执行效率低。最好不要用。

然而自己用累乘法写的朴素函数执行效率低下,时间复杂度 o\left ( n \right ) 。

2^{k}-ary 算法 (Russian\, Peasant\, algorithm) , 是一种高效的快速幂算法,时间复杂度 o\left ( \log N \right ) 。

原理:

求 x^{n} 时,朴素方法需要累乘 n 次。假如 n 是 2 的指数型,可以表示为 n=2^{k} ,所以可以转化成:

x^{n}=\left ( \left ( x^{2} \right ) ^{2}\right )\cdots 

就压缩到了 k 次平方运算。

可以把这个方法推广到任意 n , 把 n 转化为二进制的形式:

n=2^{k1}+2^{k2}+2^{k3}+2^{k4}+\cdots  

那么

x^{n}=x^{^{2^{k1}}}x^{^{2^{k2}}}x^{^{2^{k3}}}x^{^{2^{k4}}}\cdots

所以只需要求到最高位的 x^{^{2^{k}}} 便可。

举个例子,求  x^{37}   , 37 的二进制为 100101 ,每个 1 的权依次是 32 、4、1 ,所以就有:

x^{37}=x^{32}x^{4}x^{1}

代码:

ll mod_pow(ll x, ll n, ll mod) {   //挑战
    ll res = 1;
    while (n > 0) {
        if (n & 1) res = res * x % mod;
        x = x * x % mod;
        n >>= 1;
    }
    return res;
}

还有一种递归的实现:

ll mod_pow(ll x, ll n, ll mod) {    
    if (n == 0) return 1;
    ll res = mod_pow(x * x % mod, n / 2, mod);
    if (n & 1) res = res * x % mod;
    return res;
}

大数乘法取模:

有一点需要注意,就是在计算 x * x % mod 和 res * x % mod 时,由于 x 与 res 都会很大,直接相乘可能溢出 long long 的数据范围,此时就需要用大数乘法取模的方法。

代码:

typedef long long  ll;

ll mod_mult(ll a, ll b, ll mod) { 
    a %= mod;
    b %= mod;
    ll ans = 0;
    while (b > 0) {
        if (b & 1) {
            ans += a;
            if (ans >= mod)
                ans -= mod;
        }
        a <<= 1;
        if (a >= mod) a = a - mod;
        b >>= 1;
    }
    return ans;
}

 

你可能感兴趣的:(ACM,数论,大数,理论分析)