Barrett 约简
Barrett 约简是一种对于固定模数 \(n\) 快速计算
\[c=a\bmod n
\]
的方法。
BF
众所周知地,我们定义模运算
\[a\bmod n=a-\left\lfloor\dfrac{a}{n}\right\rfloor\times n
\]
因此,暴力计算模运算需要计算除法,考虑除法器设计复杂,并且可能不是恒定时间指令,时间和安全性都不优。
Algo 1
假设固定模数 \(n\),并且除以 \(R\) 可以快速计算(后者在普通计算机上可以取 \(2\) 的整数次幂,实现为右移运算)。
Barrett 约简的思想是使用 \(\dfrac{m}{R}\) 估计 \(\dfrac{1}{n}\)。容易得到 \(m=\dfrac{R}{n}\),为了使它成为整数,我们将其取下整,由于 \(n\) 是固定的,因此 \(m\) 可以预处理。
需要指出 Barrett 约简后结果的值域由 \([0,n)\) 变为 \([0,2n)\),这是精度丢失导致的。
更具体的精度要求是
\[a\lt n^2\lt R.
\]
对于 \(32\) 位整型的计算,要求 \(R\) 取 \(2^{64}\),这样 \(m\) 可以有 \(32\) 位以上的精度,注意过程中涉及 \(128\) 位整型的计算。
struct Barrett {
int64_t m, p;
void init(int pp) {
m = ((__int128)1 << 64) / pp;
p = pp;
}
int64_t operator()(int64_t x) {
return x - ((__int128(x) * m) >> 64) * p;
}
} mod;
Algo 2
对于模乘运算,例如 \(c=a\times b\bmod n\),假设 \(b,n\) 均固定,我们可以预处理 \(\left\lfloor\dfrac{bR}{n}\right\rfloor\),然后使用
\[ab-\left\lfloor\dfrac{a\left\lfloor\dfrac{bR}{n}\right\rfloor}{R}\right\rfloor
\]
代替 \(ab\bmod n\)。
思想与 \(\text{Algo 1}\) 类似。