线性同余方程-扩展欧几里得算法

完整代码见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

 

扫描二维码关注公众号,回复: 2190815 查看本文章

解法推理(扩展欧几里得算法求特解)

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

由裴蜀定理(对任何整数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

猜你喜欢

转载自blog.csdn.net/hffhjh111/article/details/81063458
今日推荐