欧几里德算法 贝祖等式 扩展欧几里德算法

欧几里德算法 贝祖等式 扩展欧几里德算法

计算机程序设计艺术开篇便提到了“欧几里德”算法,也就是中国的“辗转相除法”。
这个是个古老、经典的算法。
说的是:两个正整数 a,b(设a>b)求出他们的最大公约数。
算法步骤:
1. r=a mod b    (mod是相除取余数的意思)
2.如果r=0,则b是最大公约数,否则转3
3.a=b ,b=r  转 1
算法的一种证明:
证:
    要证欧几里德算法成立,即证: gcd(a,b)=gcd(b,r),其中 gcd是取最大公约数的意思,r=a mod b
    下面证 gcd(a,b)=gcd(b,r)
    设  c是a,b的最大公约数,即c=gcd(a,b),则有 a=mc,b=nc,其中m,n为正整数,且m,n互为质数
    由 r= a mod b可知,r= a- qb 其中,q是正整数,
    则 r=a-qb=mc-qnc=(m-qn)c
    b=nc,r=(m-qn)c,且n,(m-qn)互质(假设n,m-qn不互质,则n=xd, m-qn=yd 其中x,y,d都是正整数,且d>1
                                                                则a=mc=(qx+y)dc, b=xdc,这时a,b 的最大公约数变成dc,与前提矛盾,
                                                                 所以n ,m-qn一定互质)
    则gcd(b,r)=c=gcd(a,b)
    得证。

定理2:(由法国数学家拉梅证明)欧几里得算法所需除法次数不超过m和n中较小的那个数的十进制位数的5倍

定理3:欧几里得算法所需除法次数不超过2 log(n+1),其中n为较小的数


由欧几里德算法,衍生出来一个重要的贝祖等式:两数的最大公约数可以用两数的整数倍相加来表示,如21 = gcd(105,252)=5 × 105 + (−2) × 252。
贝祖等式的 非严格证明:
证明:
我们可以将辗转相除法的步骤看成:
a      = p(0)b + r(0)
b      = p(1)r(0)+r(1)
r(0)   = p(2)r(1)+r(2)
r(1)   = p(3)r2+r(3)
......
r(n-2)= p(n)r(n-1)+ r(n)
r(n-1)= p(n+1)r(n)+ r(n+1)
r(n)   = p(n+2)r(n+1)
当我们做到最后一个式子的时候,我们就知道 gcd(a,b)=r(n+1);
由倒数第二个式子,我们可知:r(n+1) =gcd(a,b)=r(n-1) - p(n+1)r(n),其中r(n)又可以用上面的式子r(n)=r(n-2)-p(n)r(n-1)代替,就这样不断地用上面的式子代替,
到最后我们就可以得到gcd(a,b)关于a,b的一次的关系式。
得证.

现在我们已经知道 存在 x,y使得 ax+by=gcd(a,b)成立了,那么怎样更直接地求x,y呢?
设: a*x1+b*y1=gcd(a,b)
       b*x2+(a mod b)*y2=gcd(b,(a mod b))
我们已经知道了:gcd(a,b)=gcd(b,(a mod b))
那么有:a*x1+b*y1=b*x2+(a mod b)*y2=b*x2+(a-a/b*b)*y2=b*x2 +a*y2- a/b*b*y2=a*y2+b*(x2-a/b*y2)
那么我们有:x1=y2 ;   y1=x2-a/b*y2, 这样就把求x1,y1的问题,转化为求b*x2+(a mod b)*y2=gcd(a,b)中x2,y2的问题。
这个思路是基于递归的思想的。因为 gcd 不断的递归求解一定会有个时候 b=0,所以递归可以结束。
基于递归实现的C++代码:
#include < iostream >
using   namespace  std;
int  x = 0 ,y = 0 ,gcd = 0 ;
void  qq( int  , int );
int  main()
{   
    
int  a,b;
    cin
>> a >> b;//输入的时候保证a>b,a,b是正整数就好了就好了,我这里没再检查a,b的有效性。
    qq(a,b);
    cout
<< x << " * " << a << " + " << y << " * " << b << " = " << x * a + y * b << " = " << gcd << endl;


    
return   0 ;
}
void  qq( int  a, int  b)
{
    
if ( 0 == b)
    {
        x
= 1 ;
        y
= 0 ;
        gcd
= a;
        
return ;
    }
    
else
    {
        qq(b,a
% b);
        
int  temp = x;
        x
= y;
        y
= temp - a / b * y;
        
return ;
    }

}

循环版本:
#include < iostream >
using   namespace  std;

void  qq( int  , int );
int  main()
{   
    
int  a,b;
    cin
>> a >> b;
    qq(a,b);

    
return   0 ;
}
void  qq( int  a, int  b)
{
    
int  x = 1 ,y = 0 ,gcd = 0 ;  //
    
int  temp[ 100 ][ 2 ];    //记录过程中的a,b
    
int  counter = 0 ;
    
while (b != 0 )
    {
        temp[counter][
0 ] = a;
        temp[counter][
1 ] = b;
        counter
++ ;
        
int  tempnum = a;
        a
= b;
        b
= tempnum % b;
    }

    gcd 
= a;
    cout
<< gcd << endl;
    
for ( int  i =-- counter;i >= 0 ;i -- )
    {   
        
int  tempnum = x;
        x
= y;
        y
= tempnum - temp[i][ 0 ] / temp[i][ 1 ] * y;
    }
   cout
<< x << " * " << temp[ 0 ][ 0 ] << " + " << y << " * " << temp[ 0 ][ 1 ] << " = " << x * temp[ 0 ][ 0 ] + y * temp[ 0 ][ 1 ] << " = " << gcd << endl;

}
以上求解x,y的过程我们不妨把它称为“扩展欧几里德算法”,当然我这种说法很不好,为什么呢?
在《计算机程序设计艺术》第一卷 1.2 数学准备 这个小节里面,Knuth先生给出了一个十分优美的“扩展欧几里德算法”
如下:


扩展的欧几里德算法:给定两个正整数m和n,我们计算它们的最大公约数和两个整数a和b,使am+bn=d;
E1:【初始化】a0=b=1; a=b0=0; c=m;d=n;
E2:【除】q=c/d;r=c%d
E3:【余数为0?】如果 r=0;算法终止,在这种情况下,我们如愿地有:am+bn=d;
E4:【循环】c=d;  d=r; t=a0;  a0=a;   a=t-qa;    t=b0;    b0=b;   b=t-qb,    返回E2
END;

关于这个算法,Knuth先生也给出了优美的证明,我这里不再赘言;
下面给出实现代码:

#include < iostream >

using   namespace  std;

void  qq( int  , int );
int  main()
{   
    
int  m,n;
    cin
>> m >> n;//这里同样没有检查m、n有效性,自己保证m>n,都是正整数
    qq(m,n);

    
return   0 ;
}
void  qq( int  m, int  n)
{
    
int  a = 0 ,b = 1 ,a0 = 1 ,b0 = 0 ;
    
int  c = m,d = n;

    
int  q = c / d;
    
int  r = c % d;

    
while (r != 0 )
    {
         c
= d;
         d
= r;
         
int  t = a0;
         a0
= a;
         a
= t - q * a;

         t
= b0;
         b0
= b;
         b
= t - q * b;

         q
= c / d;
         r
= c % d;

    }
    cout
<< " a= " << a << "   b= " << b << "   d= " << d << endl;
}
我们可以看到这个算法的实现起来非常的简洁、明了!
求解同样的问题,算法不同,会有相当大的差别。
程序=算法+数据结构

<全文完>






你可能感兴趣的:(欧几里德算法 贝祖等式 扩展欧几里德算法)