1. 程式人生 > >線性同餘方程-擴充套件歐幾里得演算法

線性同餘方程-擴充套件歐幾里得演算法

完整程式碼見https://github.com/YIWANFENG/Algorithm-github

線性同餘方程定義

對於線性同餘方程

這個式子的意思即 (ax)%n = b  

如果 x0 是方程的一個解,那麼所有的解可以表示為:

其中d = gcd(a,n)

性質

此方程有解當且僅當 b 能夠被 a  n 最大公約數整除(記作 gcd(a,n) | b

用演算法表示即 b%gcd(a,n) == 0

解法推理(擴充套件歐幾里得演算法求特解)

由上面可知我們只要求出一個特解即可得通解。

由裴蜀定理(對任何整數a,b,m,關於未知數x,y的裴蜀等式ax +by = m, 當且僅當m為(d=gcd(a,b))的倍數時有整數解)知

ax + by = gcd(a,b)

上面這個式子是擴充套件歐幾里得演算法的核心,

推理擴充套件歐幾里得演算法

情況1:當b = 0時,明顯可知

ax+by = ax = gcd(0,a%0) = a

所欲x = 1, y = 0 即可

情況2 : 當a !=0 &&b != 0時

ax+by = gcd(a,b)

即有

gcd(a,b) = a * x1 + b * y1

gcd(b, a%b) = b * x2 + (a%b) * y2

由 a%b = a-int(a/b)*b

a * x1 + b * y1 = b * x2 + (a-int(a/b)*b) *y2

a * x1 + b * y1 = b * x2 + a * y2 –int(a/b)*b * y2

a * x1 + b * y1 = a * y2 + b * ( x2 –int(a/b) *y2 )

所以有

x1 = y2

y1 = ( x2 – int(a/b) *y2 )

由這兩個等式,我們通過可得遞迴式的擴充套件歐幾里得演算法

int gcdEx(int a,int b,int &x,int&y) {

       //擴充套件歐幾里得演算法

       //求得aX + nY = gcd(a,n) 的一個(X,Y)

       if(b!=0){

              intr = gcdEx(b,a%b,x,y);

              intt = x;

              x= y;

              y= t - a/b*y;

              returnr;

       }else {

              x= 1;

              y= 0;

              returna;

       }

}

使用擴充套件歐幾里得演算法求特解

ax%n = b(1-1

即存在x,y滿足ax+ny = b(1-2)

d = gcd(a,n) 

b/d ∈ Z

存在(X,Y)滿足 aX + nY =d (1-3)

gcdEx()可得一個(X,Y)

(1-3)/ d * b可得

 (1-4)

對比(1-2)(1-4)可得

對於

這樣我們便得到一個(1-1)的特解x

void linear_congruences(int a,int b,int n){

       //求得 ax = b (mod n)

       if(b% gcd(a,n) == 0){

              intx,y,d,c,re;

              if(n>a) {

                     c= a;

                     a= n;

                     n= c;

                     d= gcdEx(a,n,x,y);

                     re= y*b/d;

                     cout<<"s";

                     cout<<"通解"<<re<<" + Z*"<<a/d<<endl;

              }else {

                     d= gcdEx(a,n,x,y);

                     re= x*b/d;

                     cout<<"通解"<<re<<" + Z*"<<n/d<<endl;

              }     

       }else {

              cout<<("Noresult")<<endl;

       }

}

int main(int argc,char* argv[])

{

       //3x 同餘於 2 (mod6)

       linear_mod_function(3,2,6);

       //5x同餘於 2 (mod6) 

       linear_mod_function(5,2,6);

       //4x同餘於 2 (mod6) 

       linear_mod_function(4,2,6);

       //10x同餘於 2 (mod6) 

       linear_mod_function(6,2,4);

   //cin.get();

   return 0;

}

例題-旅行青蛙

兩隻住在地球同一緯線上的青蛙想要見面,他們都選擇向西跳,直至碰面(兩隻青蛙在同一點),兩隻青蛙分別叫做青蛙A和青蛙B,並且規定緯度線上東經0度處為原點,由東往西為正方向,單位長度1米,這樣我們就得到了一條首尾相接的數軸。設青蛙A的出發點座標是x,青蛙B的出發點座標是y。青蛙A一次能跳m米,青蛙B一次能跳n米,兩隻青蛙跳一次所花費的時間相同。緯度線總長L米。我們需要判斷青蛙是否可以會面,如果可以則需要跳多少次。

Input:

輸入只包括一行5個整數x,y,m,n,L,其中x≠y < 2000000000,0 < m、n < 2000000000,0 < L < 2100000000。

Output:

輸出碰面所需要的跳躍次數,如果永遠不可能碰面則輸出一行"Impossible"

如果青蛙能夠相遇,則x + m*s – (y + n*s) = L * z

s為步數,z為一個整數

變形得(m-n)s = (x-y) (mod L)

對linear_congruences((m-n),(x-y),L){ …}稍加修改即可

void POJ() {

       intX,Y,M,N,L,a,n,b;

       cin>>X>>Y>>M>>N>>L;

       a= (M-N);

       n= L ;

       b= -(X-Y);

       if(b%gcd(a,n)==0){

             

              intx,y,d,c,re;

              if(n>a) {

                     c= a;

                     a= n;

                     n= c;

                     d= gcdEx(a,n,x,y);

                     re= y*b/d;

                     while(re>0)re-=abs(a/d);

                     re+=abs(a/d);

                     cout<<re<<endl;

              }else {

                     d= gcdEx(a,n,x,y);

                     re= x*b/d;

                     while(re>0)re-=abs(n/d);

                     re+=abs(a/d);

                     cout<<re<<endl;

              }     

       }else {

              cout<<"Impossible"<<endl;

       }

}

Ref

線性同餘方程
https://zh.wikipedia.org/wiki/%E7%BA%BF%E6%80%A7%E5%90%8C%E4%BD%99%E6%96%B9%E7%A8%8B
擴充套件歐幾里得演算法
https://zh.wikipedia.org/wiki/%E6%89%A9%E5%B1%95%E6%AC%A7%E5%87%A0%E9%87%8C%E5%BE%97%E7%AE%97%E6%B3%95
裴蜀定理
https://zh.wikipedia.org/wiki/%E8%B2%9D%E7%A5%96%E7%AD%89%E5%BC%8F
現代魔法學院
http://www.nowamagic.net/academy/detail/40110128