快速乘
快速乘
在我们进行乘法运算的时候,很多情况下会爆\(long\:long\),所以我们急需寻找一种高效的方法来完成乘法运算并且不会爆\(long\:long\)。
一、复杂度为\(o(logN)\)的快速乘
移位快速乘,思想是把\(a\times b\%p\)中的\(b\),对其进行二进制拆分,把\(b\)拆成二进制形式。\(a\times b=C_{k-1}\times a\times2^{k-1}+C_{k-2}\times a\times2^{k-2}+\dots+C_{0}\times a\times2^{0}\),其中\(c\)表示二进制数位。
例如:\(21\times13\),将\(13\)转化成二进制\(1101\),则就是\(20\times2^0+20\times2^2+20\times2^3\)。(类似于快速幂)
typedef long long ll;
ll qmul(ll n, ll b, ll mod){
ll res = 0;
while(n){
if(n & 1) res = (res + b) % mod;
b = (b + b) % mod;
n >>= 1;
}
return res % mod;
}
二、复杂度为\(O(1)\)的快速乘
利用的是很简单的一个浮点数类型。\(long\:double\)能保证数字的精确程度,然后再把这个数字进行计算。计算的方式很简单,\(a\times b\%p=a\times b-[\frac {a\times b}{p}]\times p\)。
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
ll qmul(ll x, ll y, ll mod){
ll temp = (ld)x / mod * y;
ll res = (ull)x * y - (ull)temp * mod;
return (res + mod) % mod;
}
看到这份代码有没有感到十分奇怪? 它直接用了乘法操作的啊? 这不直接爆了吗?但是它就是可以直接算出正确答案来。因为它很巧妙的运用了自动溢出这个操作,我们的代码中的\(temp\)就表示 \(⌊\frac{x\times y}{p}⌋\),所以我们要求的就变成了 \(x\times y−⌊\frac{x\times y}{p}⌋\times p\) ,虽然这两个部分都是会溢出的,但(\(unsigned\))保证了它们溢出后的差值基本不变,所以即使它会溢出也不会影响最终结果的!