总结一些科技
主要收录比较神仙的,实用的算法技巧。
快速取模
原理
找到一个近似 $m^{-1}$ 的形如 $m’>>k$ 的数。
不妨就取 $k=64,m’=\lceil\frac{2^{64}}{m}\rceil$
然后 $a\%b = a-a\times\lfloor\frac{a}{b}\rfloor = a-(a\times m’>>64)$
纯纯的整数运算,经过误差分析,可以知道后式结果最多多减去一个 $m$,判断掉就行。
因为 $a$ 常常是 $\text{long long}$ 级别的数,所以开 __int128
优化据说有 $5-6$ 倍,如果模数是 const
,编译器会自动帮忙用这个优化。
代码
1 | struct barrett{ |
注意事项
- 为啥
im,m
用ull,uint
,因为m=2
时,会爆long long
。 - 可以重载括号。
光速乘
用于计算两个 long long 级别的数乘积对一个 long long 级别的数取模的结果。
1 | long long multi(long long a,long long b,long long mod){ |
1 | long long mul(long long A, long long B, long long P){ |
原理很简单,long double 的精度误差虽然有,但是我们 -0.5 之和核查范围变成了 [0,1],肯定不会超出这个。
C++ 标准中要求了 unsigned long long 类型在溢出后保证为原值对 $2^{64}$ 取模的结果,所以直接用就行。
第二个原理类似,在 G++ 编译器中,O2 中,保证 long long 的溢出行为有定义。
推荐用第一个,各个平台都不会出锅。
如果 $P\leq 10^{14}$ 可以改成 double
判断一下是否减少了就行。