先看一道poj上的題目:【poj1006】 Biorhythms
題意:
人自出生起就有體力,情感和智力三個生理周期,分別為23,28和33天。一個周期內有一天為峰值,在這一天,人在對應的方面(體力,情感或智力)表現最好。通常這三個周期的峰值不會是同一天。現在給出三個日期,分別對應於體力,情感,智力出現峰值的日期。然后再給出一個起始日期,要求從這一天開始,算出最少再過多少天后三個峰值同時出現。
分析:
首先我們要知道,任意兩個峰值之間一定相距整數倍的周期。假設一年的第N天達到峰值,則下次達到峰值的時間為${N+Tk}$(${T}$是周期,${k}$是任意正整數)。所以,三個峰值同時出現的那一天${(S)}$應滿足$${S = N_1 + T_1*k_1 = N_2 + T_2*k_2 = N_3 + T_3*k_3}$$
${N1,N2,N3}$分別為為體力,情感,智力出現峰值的日期, ${T1,T2,T3}$分別為體力,情感,智力周期。 我們需要求出${k1,k2,k3}$三個非負整數使上面的等式成立。
想直接求出${k1,k2,k3}$貌似很難,但是我們的目的是求出${S}$, 可以考慮從結果逆推。根據上面的等式,${S}$滿足三個要求:除以${T_1}$余數為${N_1}$,除以${T_2}$余數為${N_2}$,除以${T_3}$余數為${N_3}$。這樣我們就把問題轉化為求一個最小數,該數除以${T_1}$余${N_1}$,除以${T_2}$余${N_2}$,除以${T_3}$余${N_3}$。這就是著名的中國剩余定理,我們的老祖宗在幾千年前已經對這個問題想出了一個精妙的解法。依據此解法的算法,時間復雜度可達到${O(1)}$。
中國剩余定理
在《孫子算經》中有這樣一個問題:“今有物不知其數,三三數之剩二(除以3余2),五五數之剩三(除以5余3),七七數之剩二(除以7余2),問物幾何?”這個問題稱為“孫子問題”,該問題的一般解法國際上稱為“中國剩余定理”。具體解法分三步:
-
- 找出三個數:從3和5的公倍數中找出被7除余1的最小數15,從3和7的公倍數中找出被5除余1 的最小數21,最后從5和7的公倍數中找出除3余1的最小數70。
- 用15乘以2(2為最終結果除以7的余數),用21乘以3(3為最終結果除以5的余數),同理,用70乘以2(2為最終結果除以3的余數),然后把三個乘積相加${15*2+21*3+70*2}$得到和233。
- 用233除以3,5,7三個數的最小公倍數105,得到余數23,即${ 233\%105=23 }$。這個余數23就是符合條件的最小數。
就這么簡單。我們在感嘆神奇的同時不禁想知道古人是如何想到這個方法的,有什么基本的數學依據嗎?
我們將“孫子問題”拆分成幾個簡單的小問題,從零開始,試圖揣測古人是如何推導出這個解法的。
首先,我們假設${n_1}$是滿足除以3余2的一個數,比如2,5,8等等,也就是滿足${3*k+2(k>=0)}$的一個任意數。同樣,我們假設${n_2}$是滿足除以5余3的一個數,${n_3}$是滿足除以7余2的一個數。
有了前面的假設,我們先從${n_1}$這個角度出發,已知${n_1}$滿足除以3余2,能不能使得${n_1+n_2}$的和仍然滿足除以3余2?進而使得${n_1+n_2+n_3}$的和仍然滿足除以3余2?
這就牽涉到一個最基本數學定理,如果有${ a \% b=c }$,則有${ (a+k*b) \% b=c(k為非零整數) }$,換句話說,如果一個除法運算的余數為${c}$,那么被除數與${k}$倍的除數相加(或相減)的和(差)再與除數相除,余數不變。這個是很好證明的。
以此定理為依據,如果${n_2}$是3的倍數,${n_1+n_2}$就依然滿足除以3余2。同理,如果${n_3}$也是3的倍數,那么${n_1+n_2+n_3}$的和就滿足除以3余2。這是從${n_1}$的角度考慮的,再從${n_2}$,${n_3}$的角度出發,我們可推導出以下三點:
-
- 為使${n_1+n_2+n_3}$的和滿足除以3余2,${n_2}$和${n_3}$必須是3的倍數。
- 為使${n_1+n_2+n_3}$的和滿足除以5余3,${n_1}$和${n_3}$必須是5的倍數。
- 為使${n_1+n_2+n_3}$的和滿足除以7余2,${n_1}$和${n_2}$必須是7的倍數。
因此,為使${n_1+n_2+n_3}$的和作為“孫子問題”的一個最終解,需滿足:
-
- ${n_1}$除以3余2,且是5和7的公倍數。
- ${n_2}$除以5余3,且是3和7的公倍數。
- ${n_3}$除以7余2,且是3和5的公倍數。
所以,孫子問題解法的本質是從5和7的公倍數中找一個除以3余2的數${n_1}$,從3和7的公倍數中找一個除以5余3的數${n_2}$,從3和5的公倍數中找一個除以7余2的數${n_3}$,再將三個數相加得到解。在求${n_1}$,${n_2}$,${n_3}$時又用了一個小技巧,以${n_1}$為例,並非從5和7的公倍數中直接找一個除以3余2的數,而是先找一個除以3余1的數,再乘以2。也就是先求出5和7的公倍數模3下的逆元,再用逆元去乘余數。
這里又有一個數學公式,如果${a \% b=c}$,那么${(a*k) \% b=a \% b+a \% b+…+a \% b=c+c+…+c=k*c(k>0)}$,也就是說,如果一個除法的余數為${c}$,那么被除數的${k}$倍與除數相除的余數為${k*c}$。展開式中已證明。
最后,我們還要清楚一點,${n_1+n_2+n_3}$只是問題的一個解,並不是最小的解。如何得到最小解?我們只需要從中最大限度的減掉掉3,5,7的公倍數105即可。道理就是前面講過的定理“如果${a \%b=c}$,則有${(a-k*b) \% b=c}$”。所以${(n_1+n_2+n_3)\% 105}$就是最終的最小解。
這樣一來就得到了中國剩余定理的公式:
設正整數
兩兩互素,則同余方程組
有整數解。並且在模
下的解是唯一的,解為
其中
,而
為
模
的逆元。
中國剩余定理擴展——求解模數不互質情況下的線性方程組:
普通的中國剩余定理要求所有的互素,那么如果不互素呢,怎么求解同余方程組?
這種情況就采用兩兩合並的思想,假設要合並如下兩個方程:
那么得到:
我們需要求出一個最小的${x}$使它滿足:
那么${x_1}$和${x_2}$就要盡可能的小,於是我們用擴展歐幾里得算法求出${x_1}$的最小正整數解,將它代回${a_1+m_1x_1}$,得到${x}$的一個特解${x'}$,當然也是最小正整數解。
所以${x}$的通解一定是${x'}$加上${lcm(m1,m2)*k}$,這樣才能保證${x}$模${m_1}$和${m_2}$的余數是${a_1}$和${a_2}$。由此,我們把這個${x'}$當做新的方程的余數,把${lcm(m1,m2)}$當做新的方程的模數。(這一段是關鍵)
合並完成:
參考資料:
http://www.cnblogs.com/walker01/archive/2010/01/23/1654880.html