本系列文章將於2021年整理出版。前驅教材:《算法競賽入門到進階》 清華大學出版社
網購:京東 當當 作者簽名書:點我
公眾號同步:算法專輯
暑假福利:胡說三國
有建議請加QQ 群:567554289
丟番圖(Diophantus)是古希臘人,於公元前300年編寫的《算術》,是最早的代數書。
1. 二元線性丟番圖方程
方程\(ax + by = c\)被稱為二元線性丟番圖方程,其中\(a、b、c\)是已知整數,\(x、y\)是變量,問是否有整數解。
\(ax + by = c\)實際上是二維\(x-y\)平面上的一條直線,這條直線上如果有整數坐標點,方程就有解,如果沒有整數坐標點,就無解。如果存在一個解,就有無窮多個解。
下面的定理給出了有解的判斷條件和通解的形式。
定理[1]:設a,b是整數且\(gcd(a, b) = d\)。如果\(d\)不能整除\(c\),那么方程\(ax + by = c\)沒有整數解,如果\(d\)能整除\(c\),那么存在無窮多個整數解。另外,如果(\(x_0,y_0\))是方程的一個特解,那么所有的解(通解)可以表示為:
\(x = x_0 + (b/d)n\)
\(y = y_0 - (a/d)n\)
其中\(n\)是任意整數。
定理可以概況為:\(ax + by = c\)有解的充分必要條件是\(d = gcd(a, b)\)能整除\(c\)。
例如:
(1)方程18x + 3y = 7沒有整數解,因為gcd(18, 3) = 3,3不能整除7;
(2)方程25x + 15y = 70存在無窮個解,因為gcd(25, 15) = 5且5整除70,一個特解是\(x_0\) = 4,\(y_0\) = -2,通解是x = 4 + 3n,y = -2 - 5n。
下面借助平面圖解釋定理。
定理的前半部分:令\(a = da',b = db'\);有\(ax + by = d(a' x + b' y) = c\);如果\(x、y、a'、b'\)都是整數,那么\(c\)必須是\(d = gcd(a, b)\)的倍數,才有整數解。

定理的后半部分給出了通解的形式:\(x\)值按\(b/d\)遞增,\(y\)值按 \(- a/d\)遞增。設(\(x_0,y_0\))是一個格點(格點是指\(x、y\)坐標均為整數的點),移動到直線上另一個點(\(x_0 + ∆x,y_0 + ∆y\)),有\(a∆x + b∆y = 0\)。\(∆x\)和\(∆y\)必須是整數,(\(x_0 + ∆x,y_0 + ∆y\))才是另一個格點。\(∆x\) 最小是多少?因為\(a/d\)與\(b/d\)互素,只有\(∆x = b/d,∆y = - a/d\)時,\(∆x\)和\(∆y\)才是整數,並滿足\(a∆x + b∆y = 0\)。
下面用一個例題來加強對定理的理解。
線段上的格點數量
題目描述:在二維平面上,給定兩個格點p1 = (x1, y1)和p2 = (x2, y2),問線段p1 p2上除了p1 、p2外還有幾個格點?設x1 < x2。
題解:此題用暴力法逐一搜索格點復雜度太高,下面用丟番圖方程的定理來求解。
思路:首先利用\(p1 、p2\)把線段表示為方程\(ax + by = c\)的形式,它肯定有整數解。然后在線段范圍內,根據\(x\)的通解的表達式\(x = x_0 + (b/d)n\)(用\(y = y_0 - (a/d)n\)也一樣),當\(x_1 < x < x_2\)時,求出\(n\)的取值情況有多少個,這就是線段內的格點數量。
用\(p_1 、p_2\)表示線段,經過化簡,線段表示為:
\((y_2 - y_1)x + (x_1 - x_2)y = y_2 x_1 - y_1 x_2\)
對照\(ax + by = c\),得\(a = y_2 - y_1,b = x_1 - x_2,c = y_2 x_1 - y_1 x_2,d = gcd(a, b) = gcd(|y_2 - y_1|, |x_1 - x_2|)\)。
對照通解公式\(x = x_0 + (b/d)n\),令特解是\(x_1\),代入限制條件\(x_1 < x < x_2\),有:
\(x_1 < x_1 + (x_1 - x_2/d)n < x_2\)
當 \(- d < n < 0\)時滿足上面的表達式,此時\(n\)有\(d - 1\)種取值,即線段內有\(d - 1\)個格點。
下面是一個較難的題目。
Area poj 1265
題目描述:給出二維平面上的一個封閉的多邊形,多邊形的頂點都是格點。請計算多邊形邊界上格點數量\(j\),內部格點數量\(k\),以及多邊形的面積\(s\)。
題解:邊界上的格點\(j\)用前面的方法計算,面積\(s\)用幾何叉積計算,最后內部的格點\(k\)通過Pick定理計算。
Pick定理:在一個平面直角坐標系內,如果一個多邊形的頂點都是格點,多邊形的面積等於邊界上格點數的一半加上內部格點數再減一,即\(s = j/2 + k-1\)。
求解方程\(ax + by = c\)的關鍵是找到一個特解。根據定理的描述,解和求GCD有關,所以求特解用到了歐幾里得求GCD的思路,稱為擴展歐幾里得算法。
2. 擴展歐幾里得算法
方程\(ax + by = gcd(a, b)\),根據前一節的定理,它有整數解。擴展歐幾里得算法求一個特解\((x_0,y_0)\)的代碼如下[2]:
//返回 d = gcd(a,b); 並返回 ax + by = d的特解x,y
typedef long long ll;
ll extend_gcd(ll a,ll b,ll &x,ll &y){
if(b == 0){ x=1; y=0; return a;}
ll d = extend_gcd(b,a%b,y,x);
y -= a/b * x;
return d;
}
有時候為了簡化描述,把\(ax + by = gcd(a, b)\)兩邊除以\(gcd(a, b)\),得到\(cx + dy = 1\),其中\(c = a/gcd(a, b), d = b/gcd(a, b)\)。\(c,d\)是互素的。\(cx + dy = 1\)的通解是:\(x = x_0 + dn,y = y_0 - cn\)。
3. 二元丟番圖方程\(ax + by = c\)的解
用擴展歐幾里德算法得到\(ax + by = gcd(a, b)\)的一個特解后,再利用它求方程\(ax + by = c\)的一個特解。步驟如下:
(1)判斷方程\(ax + by = c\)是否有整數解,即\(gcd(a,b)\)能整除\(c\)。記 \(d = gcd(a,b)\)。
(2)用擴展歐幾里得算法求\(ax + by = d\)的一個特解\(x_0,y_0\)。
(3)在\(ax_0 + by_0 = d\)兩邊同時乘以\(c/d\),得:
\(a x_0 c/d + b y_0 c/d = c\)
(4)對照\(ax + by = c\),得到它的一個解(\(x_0', y_0'\))是:
\(x_0' = x_0 c / d\)
\(y_0' = y_0 c / d\)
(5)方程\(ax + by = c\)的通解:
\(x = x_0' + (b/d)n\)
\(y = y_0' - (a/d)n\)
4. 多元線性丟番圖方程
多元線性丟番圖方程\(a_1x_1 + a_2x_2 + ... a_nx_n = c\)在算法競賽中很少見。為擴展思路,這里也給出介紹。
定理:如果\(a_1,a_2,...,a_n\),是非零整數,那么方程\(a_1x_1 + a_2x_2 + ... a_nx_n = c\)有整數解,當且僅當\(d = gcd(a_1,a_2,...,a_n)\)整除\(c\)。如果存在一個解,則方程有無窮多個解。
定理可以用數學歸納法證明。
方程的計算步驟是:
(1)判斷方程是否有解。計算
\(d_2 = gcd(a_1, a_2)\)
\(d_3 = gcd(d_2, a_3)\)
\(d_4 = gcd(d_3, a_4)\)
...
\(d_n = gcd(d_{n-1}, a_n)\)
如果\(d_n\)能整除\(c\),方程有解。如果有解,繼續以下步驟。
(2)求解。把方程分解為\(n - 1\)個二元方程:
\(a_1x_1 + a_2x_2 = d_2 t_2\)
\(d_2t_2 + a_3x_3 = d_3 t_3\)
...
\(d_{n-1}t_{n-1} + a_nx_n = c\)
然后從最后一個方程開始,依次往前求解。特解容易求得,通解的表達很麻煩。