【OI】gcd 入门

gcd

定义

\(a,b \in \Z\),记 \(a\)\(b\) 的最大公约数为 \(\gcd(a,b)\),最小公倍数为 \(\operatorname{lcm}(a,b)\)

约定 \(\gcd(a,0)=a\)\(\gcd(0,0)=0\)

如何求 \(\gcd(a,b)\)

欧几里得算法

\(d \mid a,b\),则有 \(d \mid a \bmod b\),于是 \(\gcd(a,b)=\gcd(b,a \bmod b)\),称为辗转相除法,可以递归计算。

复杂度为 \(O(\log \max(a,b))\)

证明:

如果一次运算会使得某个数值减半,则它为对数复杂度。

\(a<b\),则 \(\gcd(a,b)=\gcd(b,a)\)

\(a>b\),则 \(a \bmod b\) 至少会让 \(a\) 减半。

得证。

值得注意的是,用欧几里得算法去算斐波那契数列的相邻两项会使复杂度达到最劣。

Stein算法

取模计算速度太慢了,如果换成减法和位运算就会快上很多。

更相减损术:\(\gcd(a,b)=\gcd(b,a-b)\),与辗转相除法同理。

以下为 Stein 算法的过程:

  • \(a \bmod 2=1,b \bmod 2=1\),则 \(\gcd(a,b)=\gcd(b,a-b)\)

  • \(a \bmod 2=1,b \bmod 2=0\),则 \(\gcd(a,b)=\gcd(\frac{a}{2},b)\)\(b\) 也同理。

  • \(a \bmod 2=0,b \bmod 2=0\),则 \(\gcd(a,b)=2 \gcd(\frac{a}{2},\frac{b}{2})\)

除法可以用位运算代替。

同理,根据减半原则,时间复杂度为 \(O(\log \max(a,b))\)

#define ctz __builtin_ctzll
inline int gcd(int a, int b){
	if(!a||!b) return a|b;
	if(a==1||b==1) return 1;
    int az=ctz(a),bz=ctz(b),z=min(az,bz),t; 
	b>>=bz;
    while(a) a>>=az,t=a-b,az=ctz(t),b=min(a,b),a=abs(t);
    return b<<z;
}

值得注意的是,用这个可以过基于值域预处理的gcd这道题,可见效率之高。

exgcd

二元线性丢番图方程:形如 \(ax+by=c\) 的方程,其中 \(a,b,c \in \Z\)

显然,这个方程要么无整数解,要么有无数组整数解。

如何判断这个方程有无整数解?

给出结论:方程有整数解的充要条件是 \(\gcd(a,b) \mid c\)

证明:

必要性:若方程有整数解,则 \(\gcd(a,b) \mid ax\)\(\gcd(a,b) \mid by\),于是 \(\gcd(a,b) \mid ax+by=c\)

充分性
即裴蜀定理,首先考虑方程 \(ax+by=\gcd(a,b)\),需证 \(\exist x,y \in \Z\),使方程成立。

令集合 \(S=\{k \mid ax+by=k,x,y \in \Z\}\),令 \(s\) 为集合的最小正元素,此时 \(ax_0+by_0=s\)

首先 \(\gcd(a,b) \mid s\)

其次令 \(a=ps+q,q=a \bmod s \in [0,s)\),则 \(q=a-ps=a(1-px_0)+b(-py_0)\),于是 \(q \in S\),但是 \(s\) 为最小正元素,所以\(q=0\),可得 \(s \mid \gcd(a,b)\)

综上,\(s=\gcd(a,b)\),此时 \(x=x_0,y=y_0\)

那么如何求得这个方程的解呢?

首先考虑解 \(ax+by=\gcd(a,b)\)

构造两个方程:

\[ax_1+by_1=\gcd(a,b) \]

\[bx_2+(a \bmod b)y_2=\gcd(b,a \bmod b) \]

根据辗转相除法,有:

\[ax_1+by_1=bx_2+(a \bmod b)y_2 \]

注意到 \(a \bmod b=a-\lfloor \frac{a}{b} \rfloor b\)

所以:

\[ax_1+by_1=ay_2+b(x_2-\lfloor \frac{a}{b} \rfloor y_2) \]

\(x_1=y_2,y_1=x_2-\lfloor \frac{a}{b} \rfloor y_2\)

这个可以在辗转相除中递归求解,这样我们就得到了一组特解,然后用通解公式得到所有解。

inline int exgcd(int a, int b, int &x, int &y){
	if(!b){
		x=1,y=0;
		return a;
	}
	int d=exgcd(a,b,y,x);
	y-=a/b*x;
	return d;
}

\[End \]

posted @ 2026-02-08 10:31  bbbzzx  阅读(1)  评论(0)    收藏  举报