无极之地
WUJI
无极之地。记录所思所见,慢慢写下去。
素数、素数无穷与算术基本定理
世界时钟 北京 --:--:-- 东京 --:--:-- UTC --:--:-- 伦敦 --:--:-- 纽约 --:--:-- 整除是初等数论里最先出现、也最常被调用的关系。最大公约数、最小公倍数和裴蜀定理,其实都在回答同一件事:两个整数能共同生成哪些倍数。把这几条写清楚,后面的同余和逆元才有地方站。下面把定义、证明、手算和代码放在一起。
整除在说什么
整数 a a a 整除整数 b b b ,记作
a ∣ b a \mid b a ∣ b
意思是存在整数 k k k ,使得 b = a k b=ak b = ak 。这时称 a a 是 的因数, 是 的倍数。例如 ,因为 ;但 。
a
约定任何非零整数都整除 0 0 0 ,因为 0 = a ⋅ 0 0=a\cdot 0 0 = a ⋅ 0 。反过来,0 0 0 只整除 0 0 0 :若 0 ∣ b 0\mid b 0 ∣ b ,则 b = 0 ⋅ k = 0 b=0\cdot k=0 。符号 表示不整除。
整除不看正负:a ∣ b a\mid b a ∣ b 当且仅当 ∣ a ∣ ∣ ∣ b ∣ |a|\mid|b| ∣ a ∣ ∣ ∣ b ∣ (a ≠ 0 a\neq 0 a = 0 )。因数通常取正的来讨论,但定义本身对负数同样成立,例如 − 6 ∣ 18 -6\mid 18 − 。写证明时把符号先收掉,往往更干净。
若 a ∣ b a\mid b a ∣ b 且 b ∣ c b\mid c b ∣ c ,则 a ∣ c a\mid c a ∣ c 。
若 a ∣ b a\mid b a ∣ b 且 a ∣ c a\mid c ,则对任意整数 ,有 。
第一条:存在 k , ℓ k,\ell k , ℓ 使 b = a k b=ak b = ak 、c = b ℓ c=b\ell c = b ℓ ,于是 c = a ( k ℓ ) c=a(k\ell) c = a ( k ℓ ) 。
第二条:存在 k , ℓ k,\ell k , ℓ 使 b = a k b=ak b = ak 、c = a ℓ c=a\ell c = a ℓ ,于是 b x + c y = a ( k x + ℓ y ) bx+cy=a(kx+\ell y) b x + cy = 。整除对线性组合封闭,后面几乎每条定理都要用它。
第三条:存在 k , ℓ k,\ell k , ℓ 使 b = a k b=ak b = ak 、a = b ℓ a=b\ell a = b ℓ ,于是 a = a ( k ℓ ) a=a(k\ell) a = a ( k ℓ ) 。若 ,则 ,整数只能是 ,故 。 时由 只能有 。
带余除法是整除的定量版本。对任意整数 a a a 和正整数 b b b ,存在唯一的整数 q , r q,r q , r ,使得
a = b q + r , 0 ≤ r < b a=bq+r,\qquad 0\le r<b a = b q + r , 0 ≤ r < b r = 0 r=0 r = 0 恰好就是 b ∣ a b\mid a b ∣ a 。存在性可以取 q = ⌊ a / b ⌋ q=\lfloor a/b\rfloor q = ⌊ a / b ⌋ ,再令 r = a − b q r=a-bq r = a ,则 。唯一性用反证:若另有 ,则 。左边是 的倍数,右边绝对值小于 ,只能两边都是 。
带余除法把「能不能整除」变成「余数是不是零」,也给后面的辗转相除准备好了原料:每做一次,问题里的数就变小,但公约数集合不变。
最大公约数 正整数 d d d 称为 a , b a,b a , b 的公约数,若 d ∣ a d\mid a d ∣ a 且 d ∣ b d\mid b d ∣ b 。所有公约数里最大的那个,记作 gcd ( a , b ) \gcd(a,b) g cd( a , b ) ,也常写成 。约定 ( ); 在数学上通常不定义,写程序时要单独拦住。
gcd ( a , b ) = gcd ( b , a m o d b ) \gcd(a,b)=\gcd(b,\,a\bmod b) g cd( a , b ) = g cd( b , a mod b ) 余数严格递减且非负,有限步后变成 0 0 0 ,此时回到 gcd ( a , 0 ) = ∣ a ∣ \gcd(a,0)=|a| g cd( a , 0 ) = ∣ a ∣ 。步数不超过较小那个数的位数量级,所以手算大数也只是多写几行除法,不必分解质因数。
这一步为什么成立?设 a = b q + r a=bq+r a = b q + r 。任何同时整除 a a a 和 b b b 的数,由线性组合封闭性,也整除 r = a − b q r=a-bq r = a − b q ;反过来,整除 b b 和 的数也整除 。所以 与 的公约数集合完全相同,最大者当然相同。
这立刻给出一个比「最大」更强的结论:gcd ( a , b ) \gcd(a,b) g cd( a , b ) 是所有公约数的倍数。换句话说,公约数集合恰好是 d = gcd ( a , b ) d=\gcd(a,b) d = g cd( a , b ) 的全体正因数。跟着欧几里得走即可:每一步公约数集合不变,最后一步是 gcd ( d , 0 ) \gcd(d,0) g cd( d , 0 ) ,其公约数就是 d d d 的正因数。
互素是 gcd = 1 \gcd=1 g cd= 1 的专名。两个偶数绝不互素;相邻整数一定互素,因为任何公约数都整除它们的差 1 1 1 。更一般地,gcd ( a , b ) = gcd ( a , b − a ) \gcd(a,b)=\gcd(a,b-a) g cd( a , b ) = g cd( a , b − a ) ,差不会引入新的公约数。
还有两条计算时很省事的性质。第一,gcd ( k a , k b ) = ∣ k ∣ gcd ( a , b ) \gcd(ka,kb)=|k|\gcd(a,b) g cd( k a , k b ) = ∣ k ∣ g cd( a , b ) 。把公因子提出去再算,数字立刻变小。第二,多个整数的最大公约数可以两两归约:
gcd ( a , b , c ) = gcd ( gcd ( a , b ) , c ) \gcd(a,b,c)=\gcd\bigl(\gcd(a,b),c\bigr) g cd( a , b , c ) = g cd( g cd( a , b ) , c ) 结合律成立,是因为两边描述的都是同时整除 a , b , c a,b,c a , b , c 的最大正整数。分数约分、多项式系数提取,用的都是这一套。
最小公倍数 正整数 m m m 称为 a , b a,b a , b 的公倍数,若 a ∣ m a\mid m a ∣ m 且 b ∣ m b\mid m b ∣ m 。所有正公倍数里最小的那个,记作 l c m ( a , b ) \mathrm{lcm}(a,b) lcm ( a , 。
和 gcd 对偶的一条恒等式是:对非零整数 a , b a,b a , b ,
gcd ( a , b ) ⋅ l c m ( a , b ) = ∣ a b ∣ \gcd(a,b)\cdot\mathrm{lcm}(a,b)=|ab| g cd( a , b ) ⋅ lcm ( a , b ) = ∣ ab ∣ ∣ a ∣ = ∏ i p i α i , ∣ b ∣ = ∏ i p i β i |a|=\prod_i p_i^{\alpha_i},\qquad |b|=\prod_i p_i^{\beta_i} ∣ a ∣ = i ∏ p i α i gcd ( a , b ) = ∏ i p i min ( α i , β i ) , l c m ( a , b ) = ∏ i p i max ( α i , β i ) \gcd(a,b)=\prod_i p_i^{\min(\alpha_i,\beta_i)},\qquad \mathrm{lcm}(a,b)=\prod_i p_i^{\max(\alpha_i,\beta_i)} g cd( a , b ) = i ∏ p 而 min ( α , β ) + max ( α , β ) = α + β \min(\alpha,\beta)+\max(\alpha,\beta)=\alpha+\beta min ( α , β ) + max ( α , β ) = α + β ,两边乘起来就是 ∣ a b ∣ |ab| ∣ ab ∣ 。
不想依赖唯一分解也可以。设 d = gcd ( a , b ) d=\gcd(a,b) d = g cd( a , b ) ,写 a = d a ′ a=da' a = d a ′ 、b = d b ′ b=db' b = d b ,则 。断言 。它显然是公倍数。若 是任一正公倍数,则 、 ,于是 ,从而 。因为 与 互素, 必须整除 ,故 是 的倍数。最小正者就是 。
这里用到了一次「互素则能约掉」:若 gcd ( a ′ , b ′ ) = 1 \gcd(a',b')=1 g cd( a ′ , b ′ ) = 1 且 a ′ ∣ b ′ ℓ a'\mid b'\ell a ′ ∣ ,则 。证明可以先承认裴蜀:存在 使 ,两边乘 得 ,左边两项都被 整除。不想提前用裴蜀,也可以从素因子上看: 的每个素因子都不在 里,只能进入 。
于是计算 lcm 不必另写一套辗转相除:先算 gcd,再
l c m ( a , b ) = ∣ a b ∣ gcd ( a , b ) \mathrm{lcm}(a,b)=\frac{|ab|}{\gcd(a,b)} lcm ( a , b ) = g cd( a , b ) ∣ ab ∣ 写代码时先除后乘,避免中间溢出。Python 整数没有上限,但这个习惯在其他语言里能救命。
裴蜀定理 设 a , b a,b a , b 不全为零,d = gcd ( a , b ) d=\gcd(a,b) d = g cd( a , b ) 。裴蜀定理说:存在整数 x , y x,y x , y ,使得
并且,a x + b y ax+by a x + b y 能取到的全体整数,恰好是 d d d 的倍数。
后半句几乎是整除性质的直接推论。左边是 a , b a,b a , b 的线性组合,必被 d d d 整除,所以只能落在 d d d 的倍数上。前半句保证 d d d 本身也能取到,于是所有倍数都能取到:把表示 d d d 的那组系数乘上 k k k ,就得到 d k dk d k 。
存在性有两条常见证法。一条不依赖算法,只靠良序:考虑所有正的线性组合 { a x + b y : x , y ∈ Z , a x + b y > 0 } \{ax+by:x,y\in\mathbb{Z},\,ax+by>0\} { a x + b y : x , y ∈ Z , a x + b y > 0 } ,取其中最小者 d 0 d_0 。带余除法把 除以 ,余数 仍是线性组合,且 。若 就与最小性矛盾,故 ,即 。同理 ,所以 是公约数。任何公约数又整除一切线性组合,于是 就是最大公约数。这条路同时证明了「最小正线性组合等于 gcd」。
另一条跟着欧几里得回代,也更适合写成代码。算法最后一步得到余数 d d d ,而每一步的余数都是前两个数的整数组合。形式化地,维护
r i = a x i + b y i r_i=ax_i+by_i r i = a x i + b y i 初始 r 0 = a r_0=a r 0 = a (系数 1 , 0 1,0 1 , 0 ),r 1 = b r_1=b r 1 = b (系数 )。辗转相除给出 ,于是
r i + 1 = r i − 1 − q i r i = a ( x i − 1 − q i x i ) + b ( y i − 1 − q i y i ) r_{i+1}=r_{i-1}-q_ir_i=a(x_{i-1}-q_ix_i)+b(y_{i-1}-q_iy_i) r i + 1 = r i − 1 − 最后一个非零余数就是 d d d ,对应的系数就是要的 x , y x,y x , y 。这就是扩展欧几里得算法。
一个立刻能用的推论:不定方程 a x + b y = c ax+by=c a x + b y = c 有整数解,当且仅当 d ∣ c d\mid c d ∣ c 。一次同余方程的可解性也落在同一条判别上,那是另一篇文章的事。
另一个推论:gcd ( a , b ) = 1 \gcd(a,b)=1 g cd( a , b ) = 1 当且仅当存在 x , y x,y x , y 使 a x + b y = 1 ax+by=1 a x + b y = 1 。互素因此有一个「能拼出 1 1 1 」的刻画,不必先列出全部公约数再比较。
特解一旦到手,通解是齐次部分的平移。若 a x 0 + b y 0 = c ax_0+by_0=c a x 0 + b y 0 = c ,则
x = x 0 + b d t , y = y 0 − a d t , t ∈ Z x=x_0+\frac{b}{d}t,\qquad y=y_0-\frac{a}{d}t,\qquad t\in\mathbb{Z} x = x 0 + d b t , y = 代入即得 a ⋅ ( b / d ) − b ⋅ ( a / d ) = 0 a\cdot(b/d)-b\cdot(a/d)=0 a ⋅ ( b / d ) − b ⋅ ( a / d ) = 0 ,所以加上这一项不改变线性组合的值。这也说明特解有无穷多组,扩展欧几里得只负责交出其中一组。
三个以上整数同样有裴蜀形式:gcd ( a 1 , … , a n ) \gcd(a_1,\dots,a_n) g cd( a 1 , … , a n ) 能写成 a 1 x 1 + ⋯ + a n x n a_1x_1+\cdots+a_nx_n a 1 。先对前两个做出组合,再和下一个做,归纳即可。
手算例子
例一:辗转相除并回代 求 gcd ( 252 , 198 ) \gcd(252,198) g cd( 252 , 198 ) ,并找出一组裴蜀系数。
252 = 1 ⋅ 198 + 54 198 = 3 ⋅ 54 + 36 54 = 1 ⋅ 36 + 18 36 = 2 ⋅ 18 + 0 \begin{aligned}
252&=1\cdot 198+54\\
198&=3\cdot 54+36\\
54&=1\cdot 36+18\\
36&=2\cdot 18+0
\end{aligned} 252 198 54 36 = 1 ⋅ 198 + 所以 d = 18 d=18 d = 18 。从倒数第二个等式开始回代:
18 = 54 − 1 ⋅ 36 36 = 198 − 3 ⋅ 54 18 = 54 − ( 198 − 3 ⋅ 54 ) = 4 ⋅ 54 − 1 ⋅ 198 54 = 252 − 1 ⋅ 198 18 = 4 ⋅ ( 252 − 198 ) − 198 = 4 ⋅ 252 − 5 ⋅ 198 \begin{aligned}
18&=54-1\cdot 36\\
36&=198-3\cdot 54\\
18&=54-(198-3\cdot 54)=4\cdot 54-1\cdot 198\\
54&=252-1\cdot 198\\
18&=4\cdot(252-198)-198=4\cdot 252-5\cdot 198
\end{aligned} 18 36 18 验算:4 ⋅ 252 − 5 ⋅ 198 = 1008 − 990 = 18 4\cdot 252-5\cdot 198=1008-990=18 4 ⋅ 252 − 5 ⋅ 198 = 1008 − 990 = 18 。于是 252 x + 198 y = 18 252x+198y=18 252 x + 198 y = 18 有特解 。通解加上齐次部分:
x = 4 + 11 t , y = − 5 − 14 t , t ∈ Z x=4+11t,\qquad y=-5-14t,\qquad t\in\mathbb{Z} x = 4 + 11 t , y = − 5 − 14 t , t ∈ Z 这里 11 = 198 / 18 11=198/18 11 = 198/18 ,14 = 252 / 18 14=252/18 14 = 252/18 。取 t = 0 t=0 t = 0 就是刚才那组;取 t = − 1 t=-1 t = − 1 得到 ( − 7 , 9 ) (-7,9) ,同样有 。
例二:lcm,以及拼得出与拼不出 求 l c m ( 252 , 198 ) \mathrm{lcm}(252,198) lcm ( 252 , 198 ) 。已有 d = 18 d=18 d = 18 ,所以
l c m ( 252 , 198 ) = 252 ⋅ 198 18 = 252 ⋅ 11 = 2772 \mathrm{lcm}(252,198)=\frac{252\cdot 198}{18}=252\cdot 11=2772 lcm ( 252 , 198 ) = 18 252 ⋅ 198 = 252 ⋅ 11 = 2772 验算:2772 / 252 = 11 2772/252=11 2772/252 = 11 ,2772 / 198 = 14 2772/198=14 2772/198 = 14 。因为 11 11 11 与 14 14 14 互素,不可能再缩小而不拆开其中一个因子,所以 2772 2772 2772 确是最小正公倍数。
再看 252 x + 198 y = 12 252x+198y=12 252 x + 198 y = 12 。18 ∤ 12 18\nmid 12 18 ∤ 12 ,裴蜀定理直接判无解。直观上,252 252 252 和 198 198 198 都是 18 18 18 的倍数,线性组合跨不过 18 18 这一关。
改成 252 x + 198 y = 54 252x+198y=54 252 x + 198 y = 54 。18 ∣ 54 18\mid 54 18 ∣ 54 ,有解。把例一的系数乘 3 3 3 :
252 ⋅ 12 + 198 ⋅ ( − 15 ) = 54 252\cdot 12+198\cdot(-15)=54 252 ⋅ 12 + 198 ⋅ ( − 15 ) = 54 通解是 x = 12 + 11 t x=12+11t x = 12 + 11 t ,y = − 15 − 14 t y=-15-14t y = − 15 − 14 t 。
例三:互素判定 gcd ( 35 , 18 ) \gcd(35,18) g cd( 35 , 18 ) 。
35 = 1 ⋅ 18 + 17 18 = 1 ⋅ 17 + 1 17 = 17 ⋅ 1 + 0 \begin{aligned}
35&=1\cdot 18+17\\
18&=1\cdot 17+1\\
17&=17\cdot 1+0
\end{aligned} 35 18 17 = 1 ⋅ 18 + 17 = 1 ⋅ gcd = 1 \gcd=1 g cd= 1 ,二者互素。回代:1 = 18 − 17 = 18 − ( 35 − 18 ) = 2 ⋅ 18 − 1 ⋅ 35 1=18-17=18-(35-18)=2\cdot 18-1\cdot 35 1 = 18 − 17 = 18 − ( 35 − 18 ) = 2 ⋅ 。所以
35 ⋅ ( − 1 ) + 18 ⋅ 2 = 1 35\cdot(-1)+18\cdot 2=1 35 ⋅ ( − 1 ) + 18 ⋅ 2 = 1 能拼出 1 1 1 ,就能拼出任意整数:两边乘 c c c ,得到 35 x + 18 y = c 35x+18y=c 35 x + 18 y = c 的一组解。这和「公约数只有 ± 1 \pm 1 ± 1 」是同一件事的两种说法。
想要非负解时,通解就派上用场。例如 35 x + 18 y = 1 35x+18y=1 35 x + 18 y = 1 的通解是 x = − 1 + 18 t x=-1+18t x = − 1 + 18 t 、y = 2 − 35 t y=2-35t y = 2 − 35 。取 得到 ,取 得到 ,两组里 都一正一负。要两个系数同时非负,右边必须足够大,这已经越出「有没有整数解」的范围,但步长仍然由 和 决定。
一份可直接用的实现 普通欧几里得只给 d d d 。扩展欧几里得在辗转相除的同时,把裴蜀系数也记下来。迭代写法最适合写代码:维护两组系数,使每一步的余数都能写成 a a a 、b b b 的整数组合。
from math import gcd as math_gcd
def egcd ( a : int , b : int ) -> tuple [ int , int , int ]:
""" 返回 (d, x, y),满足 d = gcd(a, b) = a*x + b*y。
约定 gcd(0, 0) = 0,系数为 (1, 0)。 """
old_r , r = a , b
old_s , s = 1 , 0
old_t , t = 0 , 1
while r :
q = old_r // r
old_r , r = r , old_r - q
跑一下就能对上前面的手算。egcd 的时间是 O ( log min ( ∣ a ∣ , ∣ b ∣ ) ) O(\log\min(|a|,|b|)) O ( log min ( ∣ a ∣ , ∣ b ∣ )) ,最坏情形接近斐波那契数那一组输入。若还要通解,特解加上 ( b / d , − a / d ) \bigl(b/d,\,-a/d\bigr) ( b / d , − a / d ) 的整数倍即可。
math.gcd 从 Python 3.5 起对负数返回非负值,lcm 里先除后乘,和上面的恒等式一致。gcd(0,0) 在 math 里是 0 0 0 ,和这里的约定对齐。负数输入不必先取绝对值再算,系数符号会跟着余数一起走,最后再按需要翻一次即可。
写代码和手算时容易踩的坑 先除后乘再取绝对值。l c m ( a , b ) = ∣ a b ∣ / gcd \mathrm{lcm}(a,b)=|ab|/\gcd lcm ( a , b ) = ∣ ab ∣/ g cd 若先算 a b ab ab ,在定长整数里可能溢出;先算 a / gcd a/\gcd a / g cd 再乘 b b b ,中间更小。
裴蜀系数不唯一,验算只看 a x + b y ax+by a x + b y 等不等于 d d d 。扩展欧几里得给出的只是其中一组,和手算回代可能差一个齐次项,两边都对。
d d d 的符号要事先约定。欧几里得对负数会走出负余数或负的 d d d ,定理陈述里通常取 d > 0 d>0 d > 0 。比较、整除判断、再乘 c / d c/d c / d 之前,先把 d d d 收成正的。
通解的步长是 b / d b/d b / d 和 − a / d -a/d − a / d ,不是 d d d 。例一里相邻特解的 x x x 相差 11 11 11 ,不是 18 18 18 。
gcd ( a , 0 ) = ∣ a ∣ \gcd(a,0)=|a| g cd( a , 0 ) = ∣ a ∣ ,但 gcd ( 0 , 0 ) \gcd(0,0) g cd( 0 , 0 ) 没有「最大」正公约数。调用方若可能传入双零,不要默默返回 0 0 0 然后继续除。
整除判断用模,不要用浮点除法。b % a == 0 对整数成立;写成 b / a 再看是不是整数,会在大数上失真。
不要把「能整除」和「互素」搞反。a ∣ b a\mid b a ∣ b 是单向的倍数关系;互素是公约数只有 ± 1 \pm 1 ± 1 。4 ∣ 12 4\mid 12 4 ∣ 12 但二者绝不互素。
约分时只约 gcd \gcd g cd ,不要误把其中一个数除尽。把 252 / 198 252/198 252/198 化成最简分数,应同时除以 18 18 18 得到 14 / 11 14/11 14/11 ,而不是看见偶数就只除 2 2 2 。
多个数求 lcm 时不要先两两相乘再除。正确做法是 l c m ( a , b , c ) = l c m ( l c m ( a , b ) , c ) \mathrm{lcm}(a,b,c)=\mathrm{lcm}(\mathrm{lcm}(a,b),c) lcm ( a , b , c ) = lcm ( lcm ( a , b ) , c ) ,每一步都用 ∣ a b ∣ / gcd |ab|/\gcd ∣ ab ∣/ g cd 。直接乘起来会把公共因子算重。
这三件事其实是一件事 整除给出线性组合封闭性,欧几里得证明公约数集合沿余数不变,裴蜀定理再把最大公约数本身写成组合。lcm 只是把同一套因数关系翻到倍数一侧。把这几条串起来,后面的同余、逆元和中国剩余定理都只是在模意义下重说一遍。
手算时先求 d d d ,再问 d d d 能不能整除目标;写代码时让扩展欧几里得同时交出 d d d 和系数。存在性、特解、通解分开,验算就对得上。把这三步养成习惯,后面遇到模逆、分数化简和周期问题,都不必再从头发明一套语言。
b
=
0 ⋅
k =
0
6
∣
18
a
∣
c
a ∣ ( b x + c y ) a\mid(bx+cy) a ∣ ( b x + cy ) 若 a ∣ b a\mid b a ∣ b 且 b ∣ a b\mid a b ∣ a ,则 a = ± b a=\pm b a = ± b 。 a ( k x +
ℓ y )
a ≠ 0 a\neq 0 a = 0
−
b q
b ( q − q ′ ) = r ′ − r b(q-q')=r'-r b ( q − q ′ ) = r ′ − r gcd ( a , 0 ) = ∣ a ∣ \gcd(a,0)=|a| g cd( a , 0 ) = ∣ a ∣ gcd ( 0 , 0 ) \gcd(0,0) g cd( 0 , 0 ) b
b
)
,
∣
b
∣
=
i ∏ p i β i
i
m i n ( α i , β i )
,
lcm
(
a
,
b
)
=
i ∏ p i m a x ( α i , β i )
′
gcd ( a ′ , b ′ ) = 1 \gcd(a',b')=1 g cd( a ′ , b ′ ) = 1 l c m ( a , b ) = d ∣ a ′ b ′ ∣ \mathrm{lcm}(a,b)=d|a'b'| lcm ( a , b ) = d ∣ a ′ b ′ ∣ m = d a ′ k = d b ′ ℓ m=da'k=db'\ell m = d a ′ k = d b ′ ℓ
b ′
ℓ
a ′ x + b ′ y = 1 a'x+b'y=1 a ′ x + b ′ y = 1 a ′ ( x ℓ ) + ( b ′ ℓ ) y = ℓ a'(x\ell)+(b'\ell)y=\ell a ′ ( x ℓ ) + ( b ′ ℓ ) y = ℓ d 0
0 , 1 0,1 0 , 1
r i − 1 = q i r i + r i + 1 r_{i-1}=q_ir_i+r_{i+1} r i − 1 = q i r i + r q i
r i
=
a ( x i − 1 −
q i x i ) +
b ( y i − 1 −
q i y i )
y 0
−
d a t , t ∈
Z
x 1
+
⋯ +
a n x n
54
= 3 ⋅ 54 + 36
= 1 ⋅ 36 + 18
= 2 ⋅ 18 + 0
54
18
= 54 − 1 ⋅ 36 = 198 − 3 ⋅ 54 = 54 − ( 198 − 3 ⋅ 54 ) = 4 ⋅ 54 − 1 ⋅ 198 = 252 −
( x , y ) = ( 4 , − 5 )
( − 7 , 9 )
252 ⋅ ( − 7 ) + 198 ⋅ 9 = − 1764 + 1782 = 18 252\cdot(-7)+198\cdot 9=-1764+1782=18 252 ⋅ ( − 7 ) + 198 ⋅ 9 = − 1764 + 1782 = 18 18
17
+
1
= 17 ⋅ 1 + 0
18
−
1 ⋅
35
t
*
r
old_s , s = s , old_s - q * s
old_t , t = t , old_t - q * t
return old_r , old_s , old_t
def bezout ( a : int , b : int ) -> tuple [ int , int , int ]:
""" 规范化:d >= 0,并与 math.gcd 对齐符号约定。 """
d , x , y = egcd ( a , b )
if d < 0 :
d , x , y = - d , - x , - y
return d , x , y
def lcm ( a : int , b : int ) -> int :
if a == 0 or b == 0 :
return 0
return abs ( a // math_gcd ( a , b ) * b )
def solve_linear_diophantine ( a : int , b : int , c : int ) -> tuple [ int , int ] | None :
""" 求 ax + by = c 的一组特解;无解返回 None。 """
d , x , y = bezout ( a , b )
if d == 0 :
return ( 0 , 0 ) if c == 0 else None
if c % d :
return None
k = c // d
return x * k , y * k
if __name__ == " __main__ " :
print ( bezout ( 252 , 198 )) # (18, 4, -5)
print ( lcm ( 252 , 198 )) # 2772
print ( solve_linear_diophantine ( 252 , 198 , 18 )) # (4, -5)
print ( solve_linear_diophantine ( 252 , 198 , 54 )) # (12, -15)
print ( solve_linear_diophantine ( 252 , 198 , 12 )) # None
print ( bezout ( 35 , 18 )) # (1, -1, 2)
i + 1
1
⋅
198
= 4 ⋅ ( 252 − 198 ) − 198 = 4 ⋅ 252 − 5 ⋅ 198
整除、最大公约数与裴蜀定理 · 无极之地