欧几里得算法和扩展欧几里得算法 数论基础

  这两个算法能够说是OI里数学模块最重要的基础了(若是位运算不算数学的话)。html

一.欧几里得算法(Euclidean Algorithm)算法

  模板水题:LOJ P1212  (LOJ真是个好东西啊)ide

  在学习一种算法前,我认为咱们首先应该知道,这种算法是要解决什么问题的。函数

  小学就已经学过了两个数的最大公约数,而欧几里得算法就是为了求出两个数a、b的最大公约数的,这个最大公约数能够表示为gcd(a,b)。学习

  欧几里得算法又称展转相除法,这个名字已经揭示了它的主要思想:展转相除!idea

  它的函数代码只有一行,简单便捷,复杂度O(log n):spa

 

    int gcd(int a,int b){ return b?gcd(b,a%b):a; }

 

  不要小看这短短的一行代码,其中蕴含了无尽的人生智慧(/滑稽)code

  由于代码很短,因此算法的过程我就不赘述了,主要就是递归,能够从代码中看出来。htm

  若是赶时间(好比距离noip就差一天了),能够忽略掉证实只记代码,可是我认为证实仍是必要的,证实在这里:证实blog

 

二.扩展欧几里得算法

  首先咱们要理解一个定理:

    贝祖定理:若存在a、b是整数,则必存在整数x、y,知足ax+by=gcd(a,b)。

  证实在这里写得比较清楚:证实

  须要耐心理解。

  贝祖定理

  证实:

    当 b=0 时,gcd(a,b)=a,此时 x=1 , y=0

    当 b!=0 时,

      设 ax1+by1=gcd(a,b)=gcd(b,a%b)=bx2+(a%b)y2

      又因 a%b=a-a/b*b

      则 ax1+by1=bx2+(a-a/b*b)y2

          ax1+by1=bx2+ay2-a/b*by2

           =ay2+bx2-b*a/b*y2

           =ay2+b(x2-a/b*y2)

      解得 x1=y2 , y1=x2-a/b*y2

    由于当 b=0 时存在 x , y 为最后一组解

    而每一组的解可根据后一组获得

    因此第一组的解 x , y 必然存在

    证毕。

  显然,由于当b=0时,x=1,y=0,这时x和y是已知的,因此咱们很容易想到经过递归来求解。

  不断返回下一层的解,来获得这一层的解,最终回溯回来,得解。

  由于是借助欧几里得算法进行回溯的,因此复杂度也是O(log n)。

  基本理解扩展欧几里得算法后,咱们就能够来看看例题了。

 

  例题:洛谷oj P1082

  同余定理:给定一个正整数$m$,若是两个整数$a$和$b$知足$a-b$可以被$m$整除,即$(a-b)/m$获得一个整数,那么就称整数$a$与$b$对模$m$同余,记做$a\ ≡\ b\ (\ mod \ m \ )$。对模$m$同余是整数的一个等价关系。

  其实就是$a\ mod\ m\ =\ b\ mod\ m$。

  当$b>=1$时,由于$1\ mod\ b\ =\ 1$,因此$ax\ ≡\ 1(mod b)$就是$ax mod b=1$。其实就差很少是方程$ax\ +by\ =\ 1$,y可能为负数,因此咱们在作exgcd后还要加个答案处理。

  ac代码:

#include <cstdio>
using namespace std;

long long a,b;

long long x,y;
inline void exgcd(long long a,long long b){
    if (b==0){
        x=1,y=0;
        return ;
    }
    else exgcd(b,a%b);
    long long x1=x;
    x=y,y=x1-a/b*y;
    return ;
}

int main(){
    scanf("%lld%lld",&a,&b);
    exgcd(a,b);
    while (x<0)
        x+=b;
    x%=b;
    printf("%lld",x);
    return 0;
}
相关文章
相关标签/搜索