模板 2018-01-27 数论 最大公因数 最小公倍数 拓展欧几里得 GCD LCM 逆元

先是最简单的最大公因数, 通常情况下我们用辗转相除法, 有两种实现方法. 使用辗转相除法的原理参见维基百科.

方法一:循环

long long int _gcd(long long int a,long long int b)
{
    if(a==0||b==0)
        return 0;
    long long int res;
    res=a%b;
    while(res)
    {
        a=b;
        b=res;
        res=a%b;
    }
    return b;
}


方法二:函数递归

long long int _gcd(long long int a,long long int b)
{
    return b?gcd(b,a%b):a;
}
还有一个函数 __gcd, 据说是编译器自带的, 但是仅限于部分编译器, 比如在杭电上面选G++有这个函数, 但是选C++会返回Compile Error.
然后就是求最小公倍数啦, 公式是 lcm( x , y ) = x * y / gcd( x , y ), 因此有函数如下

long long int _lcm(long long int a,long long int b)
{
    long long int res;
    res=a/_gcd(a,b);
    res=res*b;
    return res;
}
然后就是求多个数的gcd和lcm啦

long long int gcd_multi(long long int a[],long long int n)
{//a表示存储了这些数字的数组, n表示数字个数
    long long int res,i;
    res=a[0];
    for(i=1;i<n;i++)
    {
        res=_gcd(res,a[i]);
    }
    return res;
}

long long int lcm_multi(long long int a[],long long int n)
{//a表示存储了这些数字的数组, n表示数字个数
    long long int res,i,g;
    res=1;
    g=gcd_multi(a,n);
    for(i=0;i<n;i++)
    {
        res=res*a[i];
        res=res/g;
    }
    res=res*g;
    return res;
}
然后就是拓展欧几里得.

拓展欧几里得通常用于解形似这个样子的二元一次方程(的一个特解)

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

因此, 当然了, 这样子的方程也是可以解的 a * x + b * y = c = k * gcd( a , b )  其中, k是一个常数

你只需要先解 a * x + b * y = gcd( a , b ) 的一组 x` 和 y`, 然后 k * x` 和 k * y` 就是a * x + b * y = c 的一组特解

模板如下:

void exgcd(long long int a,long long int b,long long int *x,long long int *y)
{//仅用来解a*x+b*y=gcd(a,b)
    if(b==0)
    {
        *x=1;
        *y=0;
        return;
    }
    exgcd(b,a%b,x,y);
    long long int temp;
    temp=*x;
    *x=*y;
    *y=temp-(a/b)**y;
}

void exgcd_all(long long int a,long long int b,long long int *x,long long int *y,long long int c)
{//可以用来解a*x+b*y=c=k*gcd(a,b),k是一个整数
//方程a*x+b*y=c,有解的情况是:c%gcd(a,b)=0
    if(c==0)
    {
        *x=*y=0;//手动笑哭
        return;
    }
    long long int g;
    g=gcd(a,b);
    exgcd(a,b,x,y);
    *x=*x*(c/g);
    *y=*y*(c/g);
}
引入一个(乘法)逆元的概念, 比如我想求一个数被除以一个数再取余, 很明显你不能先把分子取余再除以分母, 然后不知道哪个人想了个办法把它变成了乘---你可以求一个分母的逆元, 然后将分子乘以这个逆元之后, 再取余, 就是你想要的结果了.

(a*b)%m==1, 我们称b 是  a 关于 1 模 m 的乘法逆元

怎么求?

化一下, 会变成 a * b + m * k = 1, 其中 k 等于多少都无所谓, 只要求得出 b 就行.

是不是跟刚刚的 a * x + b * y = 1长得颇像?

话不多说, 模板如下:

long long int niyuan(long long int a,long long int m)
{
    int x,y,g,res;
    g=gcd(a,m);
    exgcd(a,m,&x,&y);
    if(1%g!=0)
        return -1;
    x=x*1/g;
    m=abs(m);
    res=(x%m+m)%m;
    return res;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值