Barrett Reduction
巴雷特约减
不知道为什么突然想写这个。
由于朴素的取模效率过于低下,所以上 Barrett Reduction
可以在一定程度上减小常数。
容易注意到 $ x \mod M = x - \lfloor \frac{x}{M} \rfloor \times M $ 。但直接这样做仍然很慢,因为用到了除法运算。
我们知道除以 $ 2^k $ 是可以用位运算做到很快的,考虑使用右移估算除法。
\[\lfloor \frac{x}{M} \rfloor \approx \lfloor \frac{ x \times \lfloor \frac{ 2^N }{M} \rfloor }{ 2^N } \rfloor
\]
设 $ 2^N = k\times M +r , ( 0 \le r < M ) $ ,那么有 $ \lfloor \frac{x}{M} \rfloor = \lfloor \frac{ x k }{ 2^N - r } \rfloor $ , $ \lfloor \frac{ x \times \lfloor \frac{ 2^N }{M} \rfloor }{ 2^N } \rfloor = \lfloor \frac{ x k }{ 2^N } \rfloor $ 。
这样一定正确吗?其实不一定,有极小错误的概率。
一般的编译器如果对一个常量取模会自动上 Barrett Reduction
,手动优化并不是很明显。但亲测确实对卡常有帮助,效果可能和编译器有关。
CODE
struct Mod{
ll m,p;
void init(int pp){m=((__int128)1<<64)/pp; p=pp;}
ll operator () (ll x){
return x-((__int128(x)*m)>>64)*p;
}
}MOD;