Euclid算法

看了几天挑战编程的数论,颇有感触,尤其是欧几里得算法,特此记下笔记(毕竟书是借的)。   

 

整除:对于整数a和b, 若存在整数k使得a = bk, 则称b整除(divides)a(用b|a来表示)。b|a也可以说成b是a的约数,或者a是b的倍数(multiple)。

唯一分解定理:x能唯一的表示成它的素因数的乘积。

 

如果两个整数的最大公约数(greatest common divisor)(也称gcd)是1,称二者是互素(relatively prime)的。

 

Euclid算法gcd(a,b)=gcd(b,a%b)

 

Euclid算法的证明

1.如果b|a,则gcd(a,b)= b。因为如果b整除a,则存在整数k,使得a=bk,因此gcd(bk,b)=b。

2.如果存在整数t和r,使得a=bt+r,则gcd(a,b)=gcd(b,r)。因为gcd(a,b)=gcd(bt+r,b),由于bt是b所有约数的倍数,所以a和b的所有公约数都应该能整除r。

 

Euclid算法还能找出两个x和y,使得

   a*x+b*y=gcd(a,b)

求法:

我们知道gcd(a,b)=gcd(b,a'),其中a‘=a-b*floor(a/b)。根据数学归纳原理,假设我们已经找出整数x’和y‘,使得

   b*x'+a'*y'=gcd(a,b)

把a'的表达式带入上式,得到:

   b*x'+(a-b*floor(a/b))*y'=gcd(a,b)

联立得:

   a*x+b*y=b*x'+(a-b*floor(a/b))*y'

将等号右边整理下:

   a*x+b*y=a*y'+b*(x'-floor(a/b)*y')

得到

   x = y', y = x'-floor(a/b)*y'

由此我们得到了x和y的表达式。

算法边界的情况:

   a*1+0*0=gcd(a,0)

 

代码如下:

/* Find the gcd(p, q) and x,y such that p*x + q*y = gcd(p, q) */
//例如对于两个数34398和2132, 有34398*15+2132*(-242)=26
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

long gcd(long p, long q, long *x, long *y){
    long nx, ny, g;

    if(q > p) return (gcd(q, p, y, x));

    if(q == 0){
        *x = 1;
        *y = 0;
        return (p);
    }

    g = gcd(q, p%q, &nx, &ny);

    *x = ny;
    *y = (nx - (p/q)*ny);

    return g;
}

int main(){
    long p, q, x, y, g;
    p = 34398; q = 2132;
    g = gcd(p, q, &x, &y);

    printf("%ld*%ld+%ld*%ld=%ld\n", p, x, q, y, g);

    return 0;
}

 

 《算法竞赛入门经典——训练指南》上的代码(和上面一样,只是更简洁):

void gcd(LL a, LL b, LL &d, LL &x, LL &y) {
    if(!b) { d = a; x = 1; y = 0; }
    else { gcd(b, a%b, d, y, x); y -= x*(a/b); }
}

d 代表 a 和 b 的最大公约数, 找出 x、y, 使得 ax+by=d。在此前提下 |x|+|y| 取最小值。

 

//2013-04-02 20:20:12

//2013-05-27 19:50:24 增加《训练指南》上的代码。

posted on 2013-04-02 20:17  Still_Raining  阅读(918)  评论(0编辑  收藏  举报