跳到主要内容

欧几里得算法(euclidean)

欧几里得算法又称辗转相除法,是指用于计算两个非负整数 a,b 的最大公约数。

  1. 判断 b 是否为 0,若为 0,则返回 a
  2. 否则返回 gcd(b, a % b)

证明

  1. bba%ba \% b的最大公约数为cc
  2. b=k1×cb = k_1 \times c, a%b=k2×ca \% b = k_2 \times c, 且k1k1,k2k2必互质
  3. a%b=ak×b=k2×ca \% b = a - k \times b = k_2 \times c,可得a=k2×c+k×k1×c=(k2+k×k1)×ca = k_2 \times c + k \times k_1 \times c = (k_2 + k \times k_1) \times c
  4. 由上可得aabb必有一因数cc
  5. k1k1k2+k×k1k_2 + k \times k_1的最大公约数是dd
  6. 设令k2=xk_2 = x, k1=yk_1 = y, 则gcd(x+k×y,y)=dgcd(x + k \times y, y) = d
  7. y=m×dy = m \times d, x+k×y=n×dx + k \times y = n \times d, 则x=n×dk×m×d=(nk×m)×dx = n \times d - k \times m \times d = (n - k \times m) \times d
  8. b=k1×c=m×d×cb = k_1 \times c = m \times d \times c
  9. a%b=k2×c=(nk×m)×d×ca \% b = k_2 \times c = (n - k \times m) \times d \times c
  10. 由于gcd(b,a%b)=cgcd(b, a \% b) = c, 则d=1d = 1
  11. 由此可得aa % b = b % (a % b) = c

扩展欧几里得

已知aa,bb,求a×x+b×y=gcd(a,b)a \times x + b \times y = gcd(a, b)xx,yy一个解

  1. a×x+b×y=gcd(a,b)=b×x+(a%b)×ya \times x + b \times y = gcd(a, b) = b \times x + (a \% b) \times y
  2. b×x+(a%b)×y=b×x+(ak×b)×y=b×x+a×yk×b×y=a×y+b×(xk×y)=a×y+b×(xfloor(a/b)×y)b \times x + (a \% b) \times y = b \times x + (a - k \times b) \times y = b \times x + a \times y - k \times b \times y = a \times y + b \times (x - k \times y) = a \times y + b \times (x - floor(a / b) \times y)
  3. 带入上一层得x=y1x = y_1, y=x1floor(a/b)×y1y = x_1 - floor(a / b) \times y_1
  4. 由于a%b=n%0a \% b = n \% 0a×x+b×y=gcd(a,b)=n×x+0×ya \times x + b \times y = gcd(a, b) = n \times x + 0 \times y, 可求得一解x=1x = 1, y=0y = 0
  5. 从最里层开始往上回溯即可得解

核心代码

/**
* 欧欧几里得算法
* 返回a与b的最大公约数
*/
export function gcd(a: number, b: number) {
if (b) return gcd(b, a % b);
return a;
}
/**
* 扩展欧几里得算法
* 已知a,b
* 求a * x + b * y == gcd(a,b)中,x与y的一个解
*/
export function ex_gcd(
a: number,
b: number,
): {
ans: number;
x: number;
y: number;
} {
if (b === 0) return { ans: a, x: 1, y: 0 };
const res = ex_gcd(b, a % b);
[res.x, res.y] = [res.y, res.x - Math.floor(a / b) * res.y];
return res;
}

/**
* 返回a与b的最大公倍数
*/
export function lcm(a: number, b: number) {
return (a * b) / gcd(a, b);
}