zoukankan      html  css  js  c++  java
  • 欧几里得算法和扩展欧几里得算法 数论基础

      这两个算法可以说是OI里数学模块最重要的基础了(如果位运算不算数学的话)。

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

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

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

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

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

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

     

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

     

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

      因为代码很短,所以算法的过程我就不赘述了,主要就是递归,可以从代码中看出来。

      如果赶时间(比如距离noip就差一天了),可以忽略掉证明只记代码,但是我认为证明还是必要的,证明在这里:证明

     

    二.扩展欧几里得算法

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

        贝祖定理:若存在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;
    }

     

  • 相关阅读:
    centos7 安装prometheus node_exporter
    RMAN备份演练初级篇
    RMAN命令
    oracle数据库的归档模式
    oracle的会话(session)
    oracle的例程
    oracle热备份
    Oracle数据库归档模式的切换及其相关操作详解
    Oracle角色
    类名.class, class.forName(), getClass()区别
  • 原文地址:https://www.cnblogs.com/awakening-orz/p/10896923.html
Copyright © 2011-2022 走看看