zoukankan      html  css  js  c++  java
  • 一篇文章讲明白gcd,exgcd,lct,exlct

    0,取模与整除

    这一部分讨论在c++中取模与整除的特性

    取模操作,又称取余,在C++中的符号是\%,和乘除法属于同一运算优先级,模数不允许出现零
    正数模正数结果是非负数,很好理解,但出现了负数怎么办?

    #include <iostream>
    using namespace std;
    int main() {
    cout << 8 % 3 << endl;
    cout << 8 % -3 << endl;
    cout << -8 % 3 << endl;
    cout << -8 % -3 << endl;
    return 0;
    }
    /*
    2
    2
    -2
    -2
    */

    结论:取模的结果只与被模数的正负号有关

    整除操作,是做除法后舍去余数得到的整数结果,依然需要讨论,出现了负数怎么办?

    #include <iostream>
    using namespace std;
    int main() {
    cout << 8 / 3 << endl;
    cout << 8 / -3 << endl;
    cout << -8 / 3 << endl;
    cout << -8 / -3 << endl;
    return 0;
    }
    /*
    2
    -2
    -2
    2
    */

    结论:整除的结果和被除数与除数是否同号有关,商的绝对值必然等于绝对值之商,还有一个特性,就是商*除数+余数=被除数

    进行整除运算时不要使用floor(),ceil()等容易导致浮点误差的函数.

    一些常用的小技巧:

    //向上取整(仅适用于正数)
    int ceil_div(int a,int b){
    return (a+b-1)/b;
    }
    //要求结果为正的取模
    int mod_postive(int a,int b){
    if(b<0)a=-a,b=-b;
    return (a%b+b)%b;
    }

    1,gcd(Greatest Common Division),最大公约数

    使用辗转相除法(或称Euclid算法)计算

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

    使用这种写法无需额外讨论正负号,a与b的大小的问题,但若a,b符号不同,计算出的结果正负号也难以寻找规律,不过显然有abs(gcd(a,b))=gcd(abs(a),abs(b))

    没有必要讨论a,b中包含0的情况

    辗转相除法的时间复杂度是O(log n),显然,每递归一次,max(a,b)至少减小一半,不过笔者认为这个上界还是太粗糙.

    2,exgcd(Extend Grestest Common Division,扩展gcd)

    定理:

    若gcd(a,b)=g,则必然存在x,y,使得ax+by=g

    辗转相除法的过程本身就是这个定理的构造性证明,因此要求得x,y,只需要保留在运算过程中被舍弃的信息

    int exgcd(int a,int b,int &x, int &y){
    if(!b){
    x=1;
    y=0;
    return a;
    }
    ans=exgcd(b,a%b,x,y);
    int t=x;
    x=y;
    y=t-a/b*y;
    return ans;
    }

    注意,解(x,y)不是唯一的,实际上有无穷多组,因为若(x,y)是一组解,(x+kb/g,y-ka/g)也是一组解,上述算法计算出的是x,y绝对值分别小于b/g,a/g的解的其中一组.

    如果这时候a,b,x均大于0,g=1,那么a,x就互为模b意义下的逆元

    因为不可避免地会出现负数(实际上a,b,x,y至少一个为负数),因此许多exgcd题目中正负号的讨论都会让人自闭,比如青蛙的约会,此时是降低思维难度,减少分类讨论情况数的最好方法是先透彻地理解题意,确定哪个参数有符号的限制;然后抓住辗转相除法的本质,对特解做出正确的恒等变换.

    3,crt(Chinese Remain Theory,中国剩余定理)

    中国剩余定理得名于中国古代的这样一个题目

    有东西不知道多少个,三个三个数剩两个,五个五个数剩三个,七个七个数剩五个,问这堆东西有多少个

    即,求同余方程组

    x%3=2
    x%5=3
    x%7=5

    显然解不是唯一的,因为若a满足条件,a+k(3*5*7)必定满足条件

    求解这类问题就需要使用中国剩余定理

    回过头来看前面的同余方程组,要求解x,只需要找到(y1,y2,y3),使得


    y1%3=1   y1%5=0   y1%7=0
    y2%3=0   y2%5=1   y2%7=0
    y3%3=0   y3%5=0   y3%7=1

    于是很显然地,x=2*y1+3*y2+5*y3,于是问题就变成了怎么求解(y1,y2,y3)

    我们用符号rev(a,b)来表示a(或者a\%b)在取模b意义下的逆元,这个值容易通过对a,b做exgcd求得

    那么便有了


    y1=rev(5*7,3)*5*7
    y2=rev(3*7,5)*3*7
    y3=rev(3*5,7)*3*5

    需要注意的是,模数必须两两互质,否则rev(a,b)未必存在,上述方法失效.

    4,excrt(Extand Chinese Remain Theory,扩展中国剩余定理)

    如果模数不互质,同余方程组未必有解,比如模3余1,模9却余8.但未必无解,比如模3,6,9都得到2.那么对于这种情况如何求解,或者证明无解呢?将同余方程组写成如下的形式


    x%m1=a1
    x%m2=a2
    x%m3=a3
    ......
    将同余方程依次两两合并,比如第一个和第二个式子:
    x%m1=a1
          ==> x%lcm(m1,m2)=a'
    x%m2=a2
    接下来的任务就是求解这个a',求解的过程实际上是在求解同余方程组的如下等价形式
    x%m1=a1 <==> x=a1+y1m1
    x%m2=a2 <==> x=a2+y2m2
    联立
    m1y1-m2y2=a2-a1
    调用exgcd函数求解
    g=exgcd(m1,m2,y1',y2')
    如果g不是a2-a1的因数,输出无解,否则
    y1=(a2-a1)/g*y1'
    y2=(a2-a1)/g*y2'
    所以能够得到等式
    x=(a1+(m1/g)(a2-a1)y1)+y'*lcm(m1,m2)
    记a'=a1+(m1/g)(a2-a1)y1,那么我们完成了前两个式子的合并过程,得到x%lcm(m1,m2)=a',如果一直到所有式子都被合并且未出现无解的情况,最后得到的a'就是特解,a'+lcm(m1,m2....)就是通解

    5,龟速乘

    在计算excrt的时候可能会出现两个乘数和模数都在long long范围内,但是没取模的积却溢出的尴尬情况,为了防止溢出或误差,应当用类似快速幂的方式求解整数乘法

    LL turtle_mult(LL base,LL n,LL mod){
    if(n<0){
    base=-base;
    n=-n;
    }
    LL ans=0;
    while(n){
    if(n&1)ans=(ans+base)%mod;
    base=base+base%mod;
    n>>=1;
    }
    return ans;
    }

    注意,龟速乘不允许用于计数的那个乘数为负,所以一开始要特判一下

  • 相关阅读:
    二次封装CoreData
    <html>
    [bzoj1031][JSOI2007]字符加密Cipher
    kernel build &amp; preempt-rt patch &amp; xenomai
    java 微信server录音下载到自己server
    shell文本过滤编程(十一):paste命令
    经验总结21--抓取WEB数据,汇率,HtmlAgilityPack
    iOS 获取appstore 版本
    poj 3259 Wormholes 【SPFA&amp;&amp;推断负环】
    【ruby项目,语言提交检查(一)】怎样高速学习ruby ?
  • 原文地址:https://www.cnblogs.com/isakovsky/p/15340343.html
Copyright © 2011-2022 走看看