以前好像提及過關於同余問題,這里就不多講了。。。
現在我要記錄的,好像有些些復雜(當然,只是對於我來說)
語不驚人死不休!!
首先我要提及的是一次同余方程,形如 ax≡b(mod m)
首先我們要對同余方程ax≡b(mod m) 解的情況進行分析(要的解范圍要在0到m之間,不知道為啥哈哈哈)
1.當(a,m)=1時有唯一解;(默默的提一句,最大公約數)
2.當(a,m)| b時有解,解的個數是(a,m)個
3.當(a,m) 不能整除 b時,無解;
概括一下,也就是說,我們首先要求出a與m的最大公約數,如果是b的倍數,那解的個數就是(a,m),如(a,m)不能整除b,那就是無解!!!
(至於為啥是這樣的,我能力有限,就不在此討論了,也許幾年后的今天我就可以對此完善了)
那關於同余方程應該怎樣求解呢?以下給出兩種方法————形式分數求解(如果實在不懂,可從數學選修4-6,37頁自行學習)
1.乘式求解
我們換種形式,然后分子、分母同乘以與m互素的正整數n后,在分別對分子、分母%m,使用等式右邊變成整數,便得到方程唯一的解。(當然,我們要的解范圍要在0到m之間)
是不是沒聽懂??我來給你舉個例子:7x≡8(%10) 求解x
變換形式得到x≡8/7≡8*3 / 7*3≡ 24%10/21%10 ≡4/1≡4(mod10)
其中,那個3就是“與m互素的正整數”,這可以是任何數,但是我要滿足原分母(7)乘上這個數之后能模 m(就是10)之后余1(余1的目的是最后可以得到整數),所以其實核心就是經過多次變換,把分母變成1,求得解。
2.加式求解
我們依舊把他換成的形式,在分子上加上m的倍數,使新分子和分母有公約數,再約去它。最終得到的就是解。核心其實也就是讓b一直加m,一直加到能讓分子能整除分母即可;
3.擴展歐幾里得算法(在后面我會講到)
那我總結一下吧(其實是賀的老師的PPT)
1. 一般的線性同余方程為ax≡b(%m),其中a≠0,b,m均為已知數,x為未知數
2.這類方程解法:先將原方程化為ax+my=b的不定方程,根據b能否整除gcd(a,m)判斷方程是否有解,然后再用剛才說的那三種方法求解
3.若”d=(a,m) | b” ,b不能被d整除,則方程ax≡b(%m)無解, 否則在%m的完全剩余系{0,1,2....m-1}中,恰有d個解,第一個解為:
x0=x' * (b/d) %m,其余d-1個解可以通過對%n加上(m/d)的倍數得到,即xi=(x0+i*(m/d))%m, (1≤i≤d-1).(拓歐幾里得)
代碼部分:
解ax≡b(modm)方程
要求:輸入三個非零整數a,b,m空格間隔。(a,b,m整型范圍內),輸出x的同余解,如果無解輸出“no answer!”
這是解法一(擴展歐幾里得)
#include<iostream> using namespace std; int a,b,m; int ex_gcd(int a,int b,int &x,int &y) { if(b==0) { x=1,y=0;return a; } int x1,y1; int d=ex_gcd(b,a%b,x1,y1); x=y1; y=x1-a/b*y1; return d; } void mod_f(int a,int b,int m) { int d,x,y,e,i; d=ex_gcd(a,m,x,y); if(b%d!=0) { printf("no answer!"); } else { e=(x*(b/d)+m)%m; for(i=0;i<d;i++) printf("%d ",(e+i*(m/d))%m); } } int main() { cin>>a>>b>>m; mod_f(a,b,m); return 0; }
關於擴展歐幾里得算法我求了求旁邊的大佬幫我講了一遍,我好像是懂了吧,就是這個意思:
首先還要再提一下關於歐幾里得算法和裴蜀算法哈哈
1.歐幾里得算法就是輾轉相除法,即gcd(a,b)=gcd(b,a%b)。
2.裴蜀算法:
a,b是整數,如果c=gcd(a,b),那么對於任意整數x,y,a*x+b*y是c的倍數。
今天我們運用的是當gcd(a,b)|c時,x,y的不定方程a*x+b*y=c(a,b,c均為整數)有整數解無數個
下面給出證明(我分為gcd(a,b)|c和不滿足gcd(a,b)|c)
設r=gcd(a,b)
那么我們可以表示一下a=nr,b=mr,c=qr
y=(c-a*x)/b=(qr-nr*x)/mr=(q-n*x)/m
我們不難看出:若gcd(a,b)|c的話,q就是整數。 因為y=(q-n*x)/m,所以若y是整數,總有x是整數滿足這個式子(即有x使得y為整數),所以有無數組解。
然而,如果c不能整除gcd(a,b),也就是q為小數,我們還是看y=(q-n*x)/m這個式子,(其中q是小數,m、n是整數),如果y是整數,那么由於m是整數,可以推出q-n*x一定為整數,又由於q是小數、n是整數,可以推出x一定為小數。 依照此方法,我們也可以順利地推出若x是整數,y一定不是整數,即x,y一定不能同時取整。
ok,那么我們可以進入正題了,下面是正規題解(前兩步是復制粘貼的是旁邊大佬的,yyds)
首先第一步,是根據貝祖定理判斷有無整數解(gcd(a,b)|c則有整數解)。那么c=n*gcd(a,b),則設x=n*q,y=n*p。所以a*q+b*p=gcd(a,b)。
接着是第二步,是運用歐幾里得算法得到a*q+b*p=gcd(a,b)=gcd(b,a%b)=b*q`+(a-a/b*b)*p`=a*p`+b*(q`-a/b*p`)。
現在我們前后比對一下,a*q+b*p=a*p`+b*(q`-a/b*p`),可以看出,q對應的就是p’,而p對應的是q`-a/b*p`
(下面這句也是復制的大佬的)從而推出q,p的一組整數解,*n可得x,y的一組整數解,最終整數解有(x-k(b/gcd(a,b)),y-k(a/gcd(a,b)),因為k有無數個,所以整數解由無數個
最后一步我怎么着都看不懂了,就有問了問旁邊的大佬,他是那么說的:
首先我們這樣寫:
a(x-k(b/gcd(a,b)))+b(y+k(a/gcd(a,b)))=c;
我們化簡一下就會得到ax-abk/gcd(a,b)+by+abk/gcd(a,b)=c;
約一下就得到ax+by=c;
所以不難看出,對於任意的k(b/gcd(a,b))和k(a/gcd(a,b))(其中k是常數,可以是任何數)
對於x,每過一個a/gcd(a,b)就會產生一組新的解,對於y,每過一個b/gcd(a,b)就會一組新的解。
所以就會有x-k(b/gcd(a,b)),y-k(a/gcd(a,b)組解。因為k可以是任何數,所以有無數組解。
下面我再給你舉個例子:
首先,我們可以通過gcd(a,b)=gcd(b,a%b)這個遞歸解得gcd(a,b)是多少,並要記錄每一次的a/b,然后我們再看最后兩列,我們要從下往上推,先用6/3得到2,讓x=2,y=0,這樣我們就得到了最低行的等式,即ax+by=6(即3*2+0=6);就這樣,我們再讓x等於y1,讓y等於x1-[a-b]*y1(我們把x和y定義為新的解,而x1和y1為原來的解),這樣我們又得到了第二組的等式ax+by=6 (即6*0+2*3=6)那我們就能明白他的用意了,其實每一行的a,b,x,y都能滿足那個ax+by=6的等式,一開始a是3,b是0,然后一直推推推,讓x=y1,y=x1-[a-b]*y1,得到新的等式,最后a會變成99,b會變成78,此時推過來的x和y,就是可以滿足這個等式的解
其實代碼也就是這樣體現的。
這是解法二 ———形式分數加法代碼
其實思路就是我剛才講的,直接上代碼吧(還是復制PPT)
解法三————形式分數乘法
思路也是剛才提到的,也直接上代碼吧(復制PPT)
那你可能就頓悟怎么解多元一次不定方程了吧,不接受任何反駁
上例題!
輸入a,b,c,d,n 請找出一組整數解(x1,x2,x3,x4)滿足:a*x1+b*x2+c*x3+d*x4=n
思路就是這樣的: 我們先與處理掉gcd(a,b,c),然后原式就變成了 gcd(a,b,c)*y1+d*x4=n;我們運用所學知識就可以解出一組解,解出x4,d和y1;
然后列出第二組方程,a*x1+b*x2+c*x3=gcd(a,b,c)*y1 由於我們能求出gcd(a,b,c)而且已經求出了y1,我們就可以得到新的一組方程,然后一直重復以上操作,就可以得出一組解集
代碼如下
好了,終於講完了!!
太傷元氣了,我要去開擺了,再見!!