博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
扩展欧几里德算法求不定方程
阅读量:4356 次
发布时间:2019-06-07

本文共 3119 字,大约阅读时间需要 10 分钟。

  例题是 POJ 1061 青蛙的约会 

  题目大意是,一个周长为L的圆, A、B两只青蛙,分别位于 x 、y 处,每次分别能跳跃 m 、n ,问最少多少次能够相遇,如若不能输出 “ Impossible”

  此题其实就是扩展欧几里德算法-求解不定方程,线性同余方程。

  设过 k1 步后两青蛙相遇,则必满足以下等式:

    ( x + m*k1 ) - ( y + n*k1 ) = k2*L  ( k2 =0 , 1 , 2....)         //这里的k2:   存在一个整数k2, 使其满足上式

  稍微变一下形得:   ( m - n )*k1 - k2*L= y - x 

  令  a = m - n , b = -L , c = y - x. 即

    a*k1 + b*k2 = c

  转换成了线性同余方程,只要上式存在整数解,则两青蛙能相遇,否则不能。 

  欧几里德扩展原理 是解决线性同余方程的工具

  下面我们介绍  欧几里德定理的证明,以及如何推广到扩展欧几里德

 

  先来看下什么是欧几里德扩展原理:

  

  欧几里德算法又称辗转相除法,用于计算两个整数a,b的最大公约数。其计算原理依赖于下面的定理:

  定理:gcd(a,b) = gcd(b,a mod b)

  证明:

令 d = gcd( a, b )         则 d | a  ,d | b    (   x|y 表示  x能够整除y,即 y%x = 0 )        又 a可以表示成a = kb + r,则r = a mod b   前面已经知道  d | b            又  d | (a%b) => d | r => d | ( a - bk )         => ( d | a ) - ( d | bk )           所以 d | (a%b)   因此d是(b,a mod b)的公约数   因此(a,b)和(b,a mod b)的公约数是一样的,其最大公约数也必然相等,得证
 欧几里德算法就是根据这个原理来做的,其算法用C++语言描述为: 

1 int Gcd(int a, int b)2 {3   if(b == 0)    return a;4          return Gcd(b, a % b);5 }
当然你也可以写成迭代形式:

int Gcd(int a, int b) {   while(b != 0)   {        int r = b;        b = a % b;        a = r;   }   return a; }

 扩展欧几里德算法

 

  问题: 计算  a*x + b*y = Gcd( a , b )

由 欧几里德定理  得: Gcd( a , b ) = Gcd( b , a%b )  又  Gcd( b, a%b ) = b*x0 + (a%b) * y0          // 这里( x0, y0 )指 存在一对(x0,y0) 使其满足 等式  所以  a*x + b*y = b*x0 + (a%b) * y0    又 a % b = a - (a / b) * b                                      // 注:这里的 (a/b) 是程序设计语言中的除法  带入式子得:  a*x + b*y = b*x0 + [ a - (a / b) * b ] * y0  将式子右边变换下得:  a*x + b*y = a*y0 + b*( x0 - (a/b) * y0 )   根据对称我们可以知道:  x = y0 , y = x0 - (a/b) * y0  这样我们就得到了求解 x,y 的方法:x,y 的值基于 x0,y0  由此,我们可以通过类似  欧几里德定理 循环迭代地 求出 x , y   再考虑下边界条件: 因为 a*x + b*y = Gcd( a, b )     当 b = 0 时, Gcd( a, b ) = a  所以 a*x + b*y = a  所以 x = 1, y = 0  由此,我们知道边界条件为 当 b = 0 时, x = 1, y = 0
 有上面的推导过程,我们可以得出C++的实现: 

int exGcd(int a, int b, int &x, int &y)  // 此 &x 表示引用,目的是像 C中 指针那样传递地址,达到修改值的目的{  if(b == 0)  {      x = 1;      y = 0;      return a;  }  int r = exGcd(b, a % b, x, y);  int t = x;  // 因为 x 本身值要被覆盖,之后又要使用,所以定义临时变量来存储信息  x = y;  y = t - a / b * y;  return r;}

不定方程 a * x + b * y = c 的求解过程:

求 a * x + b * y = c 的整数解。    设 d = Gcd(a,b) , 若 c % d != 0  则不定方程无整数解 , 由数论相关定理可以证明    由上面的推导,我们知道,我们可以使用 ExGcd: a*x + b*y = Gcd( a, b )   求出一组解 ( x0 , y0 ) 使其满足 a * x0 + b * y0 = Gcd( a, b )    令  A = a%d , B = b%d, C = c%d  带入原式    A * x + B * y = C                        ------ 等式 1    因为 Gcd( A, B ) = 1 , 由 扩展GCD 性质得    A * xi + B * yi = Gcd( A, B ) = 1     ------ 等式2    我们可以通过 扩展GCD 求出 等式2 的一组解 ( x0, y0 )     使其满足 A * x0 + B * y0  = 1        ------- 等式3    等式3 两边同乘以 C, 可得    A * x0 * C+ B * y0 * C = C       ------- 等式4      通过观察,我们可以知道     二元组 ( x0*C, y0*C ) 是 等式1 的一组解 使其满足 A*x + B*y = C    所以 ( x0*C, y0*C ) 是 a*x + b*y = c 的一组解        另,我们将式子1 变形下:    A * ( x + B )  + B * ( y + A ) = C    我们可以知道 x , y 解有多组. 但都满足如下关系    x = x0*C + k * B    ( 其中 k 为整数 )    y = y0*C + k * A
此时方程的所有解为:x=c*k1-b*t,x的最小的可能值是0,令x=0可求出当x最小时的t的取值,但由于x=0是可能的最小取值,实际上可能x根本取不到0,那么由计算机的取整除法可知:由 t=c*k1/b算出的t,代回x=c*k1-b*t中,求出的x可能会小于0,此时令t=t+1,求出的x必大于0;如果代回后x仍是大于等于0的,那么不需要再做修正。

转至

转载于:https://www.cnblogs.com/bo-jwolf/archive/2013/04/14/3033106.html

你可能感兴趣的文章
c 语言 设计算法 例题,C语言程序设计例题.pdf
查看>>
p4 linux 命令行,《linux就该这么学》学习笔记25期2020-P4
查看>>
android9.0官方下载,安卓9.0系统刷机包 官方正式版
查看>>
android 模块封装,Android开发之针对联系人的封装
查看>>
android 构建期间错误,Android构建错误
查看>>
Android应用开发病虫害识别,基于Android平台的枣虫害识别系统的设计与实现
查看>>
织梦上传html文件,提高DedeCMS生成静态页html文件速度的方法
查看>>
html焦点自动轮播幻灯片js,js实现幻灯片轮播图
查看>>
在Logstash的配置文件中对日志事件进行区分
查看>>
安装pip
查看>>
增量+全量备份SVN服务器
查看>>
两台服务器打通了秘钥,依然无法免密登录的问题
查看>>
查看进程的准确启动时间
查看>>
在Linux下解压xz压缩文件
查看>>
关于redis闪退的案例
查看>>
Ansible-随笔-7
查看>>
Ansible随笔8
查看>>
访问nginx时验证密码
查看>>
将时间 '2018-08-06T10:00:00.000Z' 格式转化为本地时间
查看>>
为kubectl配置别名和命令行补齐
查看>>