Barrett

今天(2023/8/31)靠 Barrett 算法 效率超过了某些单 算法,也是赛时代码在 CWOI 上唯一过的一份,小记一下。

这个算法的目的是,在固定模数 的情况下,对任意的 ,计算 。要比 % 更快。

这个算法的精髓是估算。首先问题可以转化成 。我们算 有点慢,那我估算一个近似的是不是也可以?Barrett 算法就是要找一个数 使得 ,或者说 。不妨令 ,那么 ,其中

为什么是 ?因为方便算除法,直接右移即可。

现在用这个近似值估算:

假设这个 足够大(比如 ),那么有,

最后算答案:

特判一下就好了,多做一个减法而已。

当然我们找不到那么大的 ,所以得找一个合理的。显然只要 就可以了。通常 ,所以,取 就可以了。注意会爆 unsigned long long,所以中间要强转 uint128_t(uint128)。

代码

#define ull unsigned long long 
#define ui128 __uint128_t
struct Barrett {
    ull d;
    ui128 m;
    Barrett(ull _d):d(_d),m((((ui128)(1)<<64)/d)) {}
    ull operator ()(ull a) {
        ull w=(m*a)>>64;
        w=a-w*d;
        if(w>=d) w-=d;
        return w;
    }
};