龙空技术网

CSP-J/S算法探秘——裴蜀定理与拓展欧几里得算法

好课堂数学编程陈老师 21

前言:

此时各位老铁们对“c语言欧几里得算法”大致比较关心,看官们都想要分析一些“c语言欧几里得算法”的相关知识。那么小编也在网上网罗了一些关于“c语言欧几里得算法””的相关内容,希望看官们能喜欢,兄弟们快快来了解一下吧!

算法探秘:从辗转相除法到拓展欧几里得算法

辗转相除法又称欧几里得算法,是最常用的求两个正整数的最大公约数的算法,通常用于分数约分、判断两数是否互质等情况,在信息学奥赛大纲中也是难度系数3的数论知识。

但是在提高组的大纲要求中,还出现了难度系数为7的拓展欧几里得算法,这两者之间有何关系呢?加上了“拓展”二字,算法的难度有会出现什么样的变化呢?

辗转相除法(欧几里得算法)

辗转相除法的基本原理是:两个整数的最大公约数(其缩写为gcd,在初赛题目中,可以根据这个缩写,立刻判断函数的功能可能是求最大公约数)等于较小的那个数与相除余数的最大公约数。因此不断循环这个过程,最后当余数为0时,当前算式的除数就是原两个整数的最大公约数。

例如:要求91和247的最大公约数,根据辗转相除法原理,可以这么做:

247÷91=2……65

91÷65=1……26

65÷26=2……13

26÷13=2……0

因此,13是91和247的最大公约数。

辗转相除法的代码实现

根据原理的描述,大家应该能容易想到使用递归算法来解决。

其中递推公式为:

gcd(m,n) = gcd(n,m%n)

边界条件为:

当m%n==0时,n就是原来两个数的最大公约数,return n即可。

代码展示:

int gcd(int m,int n){  if(m%n==0){    return n;  }  else{    return gcd(n,m%n);  }}

除了递归算法外,我们还可以使用迭代法,不断去更新需要做取模运算的两数,直到除数为0时结束循环,次数的被除数就是最大公约数。

(对应在案例中,需要再辗转一次,即13÷0,发现除数为0进行不了,那么13就是最大公约数)

代码展示:

int gcd(int m,int n){  while(n!=0){    int r=m%n; //计算余数     m=n; //新的被除数是上次的除数     n=r; //新的除数是上次的余数   }  return m;}

以上是两种辗转相除法,即欧几里得算法的代码实现方式。

先来解决倒水问题

在了解完欧几里得算法后,大家首先来思考一个经典问题:有一个能装a升水的无刻度容器,一个能装b升水的无刻度容器,如何用这两个容器量出c升的水?

我们先带入具体的数字试试看:用3升和5升的杯子a、b量出4升水。

大家有没有想出方法来呢?

先倒满3升的杯子a,再将水全都倒到杯子b中;

再倒满3升的杯子a,再用杯子a的水将杯子b倒满,此时杯子a中还剩1升水,杯子b满;

将杯子b倒空,将杯子a剩余的水倒进去,杯子b中有1升水;

最后一次倒满3升的杯子a,再将水全部倒入杯子b,这样杯子b中就获得了4升水。

将这个过程用一个数学表达式来表示:

a*3-b*1=4(其中a=3,b=5)

虽然水杯没有刻度,但是我们可以通过重复倒满整杯水或倒出整杯水的操作,想办法倒出想要的水量,抽象成数学模型就是:

对于表达式a*x+b*y=c(其中a,b,c已知且为整数),是否存在整数x,y,使等式成立。

法国数学家艾蒂安·裴蜀对这个问题给出了定理和证明。

裴蜀定理

如果a和b的最大公约数为c,即gcd(a,b) = c,那么一定存在一对整数x和y,使等式a*x+b*y=c成立。

其中有一个特别的定理:如果a、b两数互质,那么一定存在一对整数x、y,能让a*x+b*y=1成立。

根据这个定理,我们对倒水问题可以得出两个结论:

(1)如果a和b的最大公约数是c的整数倍,那么可以用a杯和b杯倒出c升水;

(2)如果a、b两数互质,那么我们可以用a、b这两个杯子倒出任意容量的水。

此外,还有一个与拓展欧几里得算法相关的结论:

若a*x+b*y=c(其中c为a和b的最大公约数),根据辗转相除法,b与a%b的最大公约数也是c,那么可以得到等式:

a*x+b*y=b*x1+(a%b)*y1

=b*x1+(a-a/b)*y1(注:这里和下面的a/b都是a整除b)

整理等式,可得:

a(x-y1)+b(y-(x1-(a/b)*y1)=0

为了让等式成立,当且仅当x=y1并且y=x1-(a/b)y1满足。

拓展欧几里得算法

那么,怎么求使a*x+b*y=c等式成立的一对整数x、y呢?这得和欧几里得算法结合起来。

我们知道辗转相除法的过程中会出现多对数字相除,这些数字就是求整数x、y的解的关键。

还是来举例说明吧。求让5*x+3*y=4等式成立的x与y,首先分析5和3辗转相除的过程:

5÷3=1……2

3÷2=1……1

2÷1=2……0

中间出现了(3,2)、(2,1)、(1,0)这三对数字,依次进行反向辗转相除:

(1)先求1*x+0*y=4的一组解,能容易求出x = 4,y = 0;

(2)再求2*x+1*y=4的一组解,根据上面裴蜀定理的分析,x=y1,因此可以让x=上一组的y=0,从而求得y=4;

(3)再求3*x+2*y=4的一组解,同样道理,x=上一组的y =4 ,从而求得y=-4;

(4)最后求5*x+3*y=4的一组解,x=上面的y = -4,求得y = 8;

(5)至此,能让5x+3y=4成立的一组解求出来了,即x=-4,y=8。

这里有同学可能会好奇,在案例中,我们通过模拟倒水,求出的x=-1,y=3,为什么用拓展欧几里得算法求得的是x=-4,y=8呢?

这是由于5和3是互质的,因此拓展欧几里得算法计算的是根据裴蜀定理5*x+3*y=1的解,然后再将答案扩大4倍的结果;

而x=-1,y=3只是我们根据具体情境凑出来的解,满足了裴蜀定理的公式,与拓展欧几里得算法没有关系。

拓展欧几里得算法

拓展欧几里得算法是通过递归算法来求满足a*x+b*y=gcd(a,b)等式的x与y。

递归的边界条件为:辗转相除到最后会出现求gcd(gcd(a,b),0)的式子,即gcd(a,b)÷0,此时对应的解为x=1,y=0;

递推公式为:

若已知b * x1 + (a%b) * y1 = gcd(a,b)的解,那么原式中

x = y1;

y =x1-(a/b)*y1(注:这里的a/b是a整除b)

因此,对应的代码为:

int exgcd(int a, int b, int &x, int &y){//返回值是a与b的最大公约数//x与y的答案需要用引用证明符来跟随递归变化   if(!b){ //边界条件    x=1, y=0;    return a;  }  int d=psgcd(b,a%b,y,x);   //递归完成后,此时x与y的值为x1与y1的值,但因为参数中x与y交换了位置,因此最终的x=y1已经实现,就差计算y的值了。  int x1=y, y1=x;  y=x1-(a/b)*y1;  return d;}

标签: #c语言欧几里得算法

上一篇二叉树

下一篇alike 和 like区别