Binary GCD 学习笔记
算是一点杂项吧,感觉没什么机会用上。
0x00 前言
有时你需要大量且快速的求 \(\gcd\),像P5435。但是对值域预处理 \(\gcd\) 又很麻烦,这时候我们可以考虑 Binary GCD。
0x01 原理
Binary GCD 复杂度为 \(\mathcal{O}(\log n)\),实际上与一般做法相同。但是由于实现大量使用位运算,常数较小,在极端情况下更优。
0x02 方法
假设现在我们需要求 \(\gcd(a,b)\),此时可以分成以下 5 种情况:
-
\(a=b\):显然 \(\gcd(a,b)=a\);
-
\(a=0\lor b=0\):当 \(b=0\) 时 \(\gcd(a,b)=a\),\(a=0\) 时 \(\gcd(a,b)=b\);
-
\(a,b\equiv0\pmod2\):\(\gcd(a,b)=2\gcd(\dfrac{a}{2},\dfrac{b}{2})\);
-
\(a\equiv0\pmod2\lor b\equiv0\pmod2\):不妨设 \(a\equiv0\pmod2\),显然 2 不是公约数之一,有 \(\gcd(a,b)=\gcd(\dfrac{a}{2},b)\);
-
\(a,b\equiv1\pmod2\):不妨设 \(a>b\)。因为 \(\gcd(a,b)=\gcd(a−b,b)\),此时转化为上一种情况,有 \(\gcd(a,b)=\gcd(\dfrac{a-b}{2},b)\)。
0x03 优化
根据上文,有原始实现:
点击查看代码
int gcd(int a,int b){
if(a==b)return a;
if(!a)return b;
if(!b)return a;
if(~a&1){
if(b&1)return gcd(a>>1,b);
else return gcd(a>>1,b>>1)<<1;
}
if(~b&1)return gcd(a,b>>1);
if(a>b)return gcd((a-b)>>1,b);
return gcd((b-a)>>1,a);
}
但是包含大量的递归与判断的代码还是太慢了,所以我们有优化版:
点击查看代码
int gcd(int a,int b){
int az=__builtin_ctz(a);
int bz=__builtin_ctz(b);
int z=min(az,bz);b>>=bz;
while(a){
a>>=az;int diff=a-b;
az=__builtin_ctz(diff),b=min(a,b),a=abs(diff);
}
return b<<z;
}
0x04 应用
正解应该是 \(\mathcal{O}(V)\) 预处理 \(\mathcal{O}(1)\) 的查询,但是 Binary GCD 太强了。