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 应用

P5435 基于值域预处理的快速 GCD

正解应该是 \(\mathcal{O}(V)\) 预处理 \(\mathcal{O}(1)\) 的查询,但是 Binary GCD 太强了。

posted @ 2023-03-01 20:04  xx019  阅读(92)  评论(0编辑  收藏  举报