zoukankan      html  css  js  c++  java
  • 欧几里得(辗转相除gcd)、扩欧(exgcd)、中国剩余定理(crt)、扩展中国剩余定理(excrt)简要介绍

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

    直接上gcd和lcm代码。

    1 int gcd(int x,int y){
    2     return y==0?x:gcd(y,x%y);
    3 }
    1 int lcm(int x,int y){
    2     return x*y/gcd(x,y);        
    3 }

    2.扩欧:exgcd:对于a,b,一定存在整数对(x,y)使ax+by=gcd(a,b)=d ,且a,b互质时,d=1。 x,y可递归地求得。

    我懒得改返回值类型了

    1 long long exgcd(long long a,long long b,long long &x,long long &y){
    2     long long d=a;
    3     if(b==0)  y=0,x=1;
    4     else{
    5         d = exgcd(b,a%b,y,x);
    6         y -= a/b*x;
    7     }
    8     return d;
    9 }

    求解 x,y的方法的理解:


    设 a>b。
    1,显然当 b=0,gcd(a,b)=a。此时 x=1,y=0;
    2,a>b>0 时
    设 ax1+ by1= gcd(a,b);
    bx2+ (a mod b)y2= gcd(b,a mod b);
    根据朴素的欧几里德原理有 gcd(a,b) = gcd(b,a mod b);
    则:ax1+ by1 = bx2+ (a mod b)y2;
    即:ax1+ by1 = bx2+ (a - [a / b] * b)y2
              = ay2+ bx2- [a / b] * by2;
                 = ay2+ b(x2- [a / b] *y2);

    所以:x1=y2; y1=x2- [a / b] *y2;

    这样我们就得到了求解 x1,y1 的方法:x1,y1 的值基于 x2,y2.

    这个思想是递归的,因为 gcd 不断的递归求解一定会有个时候 b=0,所以递归可以结束。

    3.中国剩余定理(Chinese remainder theorem)

    截自百度百科:

    要求模下的唯一解,关键是求逆元。

    拓展欧几里得如何求逆元: 

    当a与b互素时有 gcd(a ,b)=1
                    即得: a*x+b*y=1

               a*x ≡ 1 (mod b)

     由于a与b互素,同余式两边可以同除a 得:1*x ≡ 1/a (mod b),因此 x 是 a mod b 的逆元;

     求逆元也可单写为函数:a在模b意义下的逆元:inv(a,b);

    1 long long inv(long long a, long long b){
    2   exgcd(a,b,x,y);
    3   while(x<0) x+=b;
    4   return x;
    5 }

    51nod中还有个求乘法逆元的题,直接应用扩欧求逆元即可。

    最后上crt完整代码:

     1 long long crt(){//pri数组和re数组分别保存质数和余数 也就是上图方程组中的mi和ai
     2       long long m=1,ans=0;
     3       for(int i=0;i<n;i++){
     4           m*=pri[i];
     5       }
     6       for(int i=0;i<n;i++){
     7           long long mi=m/pri[i],x,y;
     8           exgcd(mi,pri[i],x,y); //exgcd的应用:求得逆元x
     9           ans=(ans+re[i]*x*mi)%m;//加和求模下的唯一解
    10      }
    11      while(ans<0) ans+=m;
    12      return ans;
    13 }

     例题:51nod 1079中国剩余定理 http://www.51nod.com/Challenge/Problem.html#!#problemId=1079

     1 #include <iostream>
     2 using namespace std;
     3 int n;
     4 long long pri[11],re[11];//分别保存质数,和取余的结果
     5 //利用扩展欧几里得求乘法取模运算的逆元
     6 long long exgcd(long long a,long long b,long long &x,long long &y){
     7     long long d=a;
     8     if(b==0)  y=0,x=1;
     9     else{
    10         d=exgcd(b,a%b,y,x);
    11         y-=a/b*x;
    12     }
    13     return d;
    14 }
    15 //Chinese remainder theorem
    16 long long crt(){
    17     long long m=1,ans=0;
    18     for(int i=0;i<n;i++){
    19         m*=pri[i];
    20     }
    21     for(int i=0;i<n;i++){
    22         long long mi=m/pri[i],x,y;
    23         exgcd(mi,pri[i],x,y);
    24         ans=(ans+re[i]*x*mi)%m;
    25     }
    26     if(ans<0) ans+=m;
    27     return ans;
    28 }
    29 int main(){
    30     cin>>n;
    31     for(int i=0;i<n;i++){
    32         cin>>pri[i]>>re[i];
    33     }
    34     cout<<crt()<<endl;
    35     return 0;
    36 }
    View Code

    4.扩展中国剩余定理(excrt)

    如果人家给的除数不是质数怎么办?就要先处理线性同余方程组了。

    我太笨了当时看了好久还是不会,现在稍微明白点了但还是迷迷糊糊,具体分析过程可以看这个dalao的blog,过程很详细:https://www.cnblogs.com/zwfymqz/p/8425731.html

    放一个参考人家修修改改写的题目吧。POJ2891

     1 #include<iostream>
     2 #include<cstdio>
     3 #define ll long long
     4 using namespace std;
     5 
     6 const ll MAXN = 1e6 + 10;
     7 ll K, C[MAXN], M[MAXN], x, y;
     8 
     9 ll gcd(ll a, ll b) {
    10     return b == 0 ? a : gcd(b, a % b);
    11 }
    12 ll exgcd(ll a, ll b, ll &x, ll &y) {
    13     ll r=a;
    14     if (b == 0) x = 1, y = 0;
    15     else{
    16         r = exgcd(b, a % b, y, x);
    17         y -= (a / b) * x;
    18     }
    19     return r;
    20 }
    21 ll inv(ll a , ll b){//求逆元
    22     exgcd(a,b,x,y);
    23     while(x<0) x+=b;
    24     return x;
    25 }
    26 
    27 int main(){
    28     while(cin>>K){
    29         for (ll i = 1; i <= K; i++) scanf("%lld%lld", &M[i], &C[i]);
    30         bool flag = 1;
    31         for (ll i = 2; i <= K; i++) {
    32             ll M1 = M[i - 1], M2 = M[i];
    33             ll C2 = C[i], C1 = C[i - 1];
    34             ll T  = gcd(M1, M2);
    35 
    36             if ((C2 - C1) % T != 0) { flag=0; break; }
    37             M[i] = (M1 * M2) / T;
    38             C[i] = ( inv( M1 / T , M2 / T ) * (C2 - C1) / T ) % (M2 / T) * M1 + C1;
    39             C[i] = (C[i] % M[i] + M[i]) % M[i];
    40         }
    41         if(flag) cout<<C[K]<<endl;
    42         else cout<<-1<<endl;
    43 
    44     }
    45     return 0;
    46 }
    View Code
  • 相关阅读:
    kafka控制测试发送接收消息
    Kafka 启动报错java.io.IOException: Can't resolve address.
    java问题 2019
    java各种面试问题
    java 架构师
    开源的13个Spring Boot 优秀学习项目!超53K星,一网打尽!
    Dubbo 18 问
    Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离。
    XMind2TestCase 工具,提供了一个高效测试用例设计的解决方案(开源)
    windows下面安装Python和pip终极教程
  • 原文地址:https://www.cnblogs.com/noobimp/p/10301297.html
Copyright © 2011-2022 走看看