Binary GCD 学习笔记

算是一点杂项吧,感觉没什么机会用上。

0x00 前言

有时你需要大量且快速的求 gcd,像P5435。但是对值域预处理 gcd 又很麻烦,这时候我们可以考虑 Binary GCD。

0x01 原理

Binary GCD 复杂度为 O(logn),实际上与一般做法相同。但是由于实现大量使用位运算,常数较小,在极端情况下更优。

0x02 方法

假设现在我们需要求 gcd(a,b),此时可以分成以下 5 种情况:

  • a=b:显然 gcd(a,b)=a

  • a=0b=0:当 b=0gcd(a,b)=aa=0gcd(a,b)=b

  • a,b0(mod2)gcd(a,b)=2gcd(a2,b2)

  • a0(mod2)b0(mod2):不妨设 a0(mod2),显然 2 不是公约数之一,有 gcd(a,b)=gcd(a2,b)

  • a,b1(mod2):不妨设 a>b。因为 gcd(a,b)=gcd(ab,b),此时转化为上一种情况,有 gcd(a,b)=gcd(ab2,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

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

posted @   xx019  阅读(157)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
点击右上角即可分享
微信分享提示