1. 程式人生 > >gcd以及exgcd入門講解

gcd以及exgcd入門講解

ont image 分享圖片 復雜 自己 整理 9.png 擴展 情況

gcd就是最大公約數,gcd(x, y)一般用(x, y)表示。與此相對的是lcm,最小公倍數,lcm(x, y)一般用[x, y]表示。

人人都知道:lcm(x, y) = x * y / gcd(x, y)

證明起來也不是很難:

技術分享圖片

(這真的是我自己寫的,因為博客園不支持這格式……)

至於gcd的求法,想必各位在高中都學過輾轉相除法和更相減損之術,這裏只講輾轉相除法(更相減損之術略慢)

首先不妨設 x ≤ y,則gcd(x, y) =gcd(x, x +y) = gcd(x, y - x).所以gcd(x, y) = gcd(y % x, x),因此可以遞歸求解。

復雜度證明:因為y % x ≤ x && x ≤ y,所以y % x < y / 2。因此在最壞情況下為O(nlogn)。(用斐波那契數列的相鄰兩個數可以達到最壞復雜度)

那麽接下來講一下擴展gcd。

exgcd可以用來判斷並求解形如ax +by = c 的方程,當且僅當gcd(a, b) | c時,存在整數解x, y。

也就是說,exgcd可以用來求解方程ax +by = gcd(a, b)

令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}

gcd以及exgcd入門講解