gcd就是最大公約數,gcd(x, y)通常用(x, y)表示。與此相對的是lcm,最小公倍數,lcm(x, y)通常用[x, y]表示。spa
人人都知道:lcm(x, y) = x * y / gcd(x, y)code
證實起來也不是很難:blog
(這真的是我本身寫的,由於博客園不支持這格式……)遞歸
至於gcd的求法,想必各位在高中都學過展轉相除法和更相減損之術,這裏只講展轉相除法(更相減損之術略慢)博客
首先不妨設 x ≤ y,則gcd(x, y) =gcd(x, x +y) = gcd(x, y - x).因此gcd(x, y) = gcd(y % x, x),所以能夠遞歸求解。class
複雜度證實:由於y % x ≤ x && x ≤ y,因此y % x < y / 2。所以在最壞狀況下爲O(nlogn)。(用斐波那契數列的相鄰兩個數能夠達到最壞複雜度)擴展
那麼接下來說一下擴展gcd。gc
exgcd能夠用來判斷並求解形如ax +by = c 的方程,當且僅當gcd(a, b) | c時,存在整數解x, y。im
也就是說,exgcd能夠用來求解方程ax +by = gcd(a, b)d3
令a = b, b = a % b,則有方程b *x1 +(a % b) * y1 = gcd(b, a % b)
又由於gcd(a, b) = gcd(a % b),且a % b = a - b * ⌊a / b⌋
則b * x1 + (a - b * ⌊a / b⌋) * y1 =gcd(a, b)
整理得:a * y1 +b * (x1 - ⌊a / b⌋ *y1) = gcd(a, b)
因此原方程中:x = y1, y = x1 - ⌊a / b⌋ *y1。因而咱們只要遞歸求出x1, y1就能求出x, y。
代碼很短
1 void exgcd(ll a, ll b, ll& x, ll& y, ll& c) 2 { 3 if(!b) {y = 0; x = 1; c = a; return;} 4 exgcd(b, a % b, y, x); y -= a / b * x; 5 }
其中c = gcd(a, b)
值得注意的是,遞歸調用的時候y的位置上傳了x,x位置上是y,也就是說,y裏存的是x1,x裏存的是y1,因此y -= a / b *y1,即y -= a / b * x。
咱們如今已經求得了ax +by = gcd(a, b)的解,那麼對於方程ax + by = c (gcd(a, b) | c)呢?
由於已經知道a *x1 +b * y1 = gcd(a, b)的解x1, y1,左右兩邊同乘以c / gcd(a, b) 得:
a * x1 * c / gcd(a, b) +b * y1 * c / gcd(a, b) = c
則原方程的一組解x2 = x1 * c / gcd(a, b), y2 = y1 * c / gcd(a, b)
由此得出解集{(x, y) | x = x2 + k * b / gcd(a, b), y = y2 - k * a / gcd(a, b), k ∈ z}