exgcd扩展欧几里得

裴蜀定理:对于一对正整数a, b, 存在非零整数x, y使得ax + by = gcd(a, b);

ax + by 一定是最大公约数gcd(x, y)的倍数。

1) 如果b = 0,gcd(a, 0) = a, a就是最大公约数ax + by = a. (x = 1, y = 0是一组解) 0和x的最大公约数就是x。

2)ax + by = gcd(a,b) = gcd(b, a % b)

by + a % b x = by + (a - a / b * b)x = ax + b(y - a / b * x) = ax + by;

x不变,y = y - a / b * x;

所以gcd(a, b, x, y)->gcd(b, a % b, y, x )

#include <iostream>
using namespace std;

int exgcd(int a, int b, int &x, int &y)
{
    if(b == 0)
    {
        x = 1, y = 0;
        return a;
    }
    
    int d = exgcd(b, a % b, y, x);
    
    y -= a / b * x;
    
    return d;
}
int main()
{
    int n;
    cin>>n;
    while(n--)
    {
        int a,b,x,y;
        cin>>a>>b;
        exgcd(a,b,x,y);
        cout<<x<<" "<<y<<endl;
    }
}

 扩展欧几里得可以求解线性同余方程:

 a ∗ ≡ (mod m)

ax和b对m的余数相同,简称同余。实际上对于两个数a和b,若存在一个数 m | (a - b), 那么 a ≡ (mod m)

 a ∗ ≡ (mod m)

 

存在一个y,使得 ax = my + b, -> ax - my = b, 令y’ = -y, 则有 ax + my’ = b, 就相当于上面的ax + by = gcd(a,b);这里的 b = m;

#include <cstdio>
typedef long long LL;
int exgcd(int a, int b, int &x, int &y)
{
    if(b == 0)
    {
        x = 1, y = 0;
        return a;
    }
    
    int d = exgcd(b, a % b, y, x);
    
    y -= a / b * x;
    
    return d;
}
int main()
{
    int n;
    scanf("%d",&n);
    while(n--)
    {
        int a,b,m;
        scanf("%d%d%d",&a,&b,&m);
        int x, y;
        int d = exgcd(a,m,x,y);
        if(b % d) puts("impossible");
        else printf("%d\n", x * b / d);
    }
}

这里的b一定等于求得的d = gcd(a,m)最大公约数的倍数,得到倍数以后再让求得的原来的系数x乘以这个倍数就得到答案了。

204. 表达整数的奇怪方式

 

#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;

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

int main()
{
    int n;
    cin>>n;
    bool has_answer = true;
    LL a1, m1;
    cin>>a1>>m1;//先读入第一个方程,然后把后面的所有方程合并进来
    for(int i = 0; i < n - 1; i++)
    {
        LL a2, m2;
        cin>>a2>>m2;
        
        LL k1, k2;
        LL d = exgcd(a1, a2, k1, k2);
        if((m2 - m1) % d) 
        {
            has_answer = false;
            break;
        }
        
        k1 *= (m2 - m1) / d;
        LL t = a2 / d;
        k1 = (k1 % t + t) % t;//把k1变成最小的正整数解
        
        //合并之后新的x = ka + m的解
        m1 = a1 * k1 + m1;
        a1 = abs(a1 / d * a2);//这两部顺序不能变
        
    }
    
    if(has_answer)
    {
        cout<<(m1 % a1 + a1) % a1<<endl;
    }
    else puts("-1");
}

x mod a1 = m1, x mod a2 = m2, x mod a3 = m3, ……,x mod an = mn

我们来试着进行合并,先合并第一和第二个式子:

x = k1a1 + m1, x = k2a2 + m2

k1a1 + m1 = k2a2 + m2  ——>>>  k1a1 - k2a2 = m2 - m1;

通过exgcd扩展欧几里得可以求出一组解(k1, k2),int d = exgcd(a1, a2, k1, k2)返回的是最大公约数,然后 (m2 - m1) / d是扩大的倍数,如果d | (m2 - m1)能够整除就有解,如果不能整除的话就不能没有解。

 构造一个式子:k1 = k1 + k * a2 / d; k2 = k2 + k * a1 / d; 将这个式子带入上面的方程有:

(k1 + k*a2/d)a1 - (k2 + k*a1/d)a2 = m2 - m1;  k1a1 - k2a2 = m2 - m1;所以是完全等价的。

所以又有k1 ≡ k1 (mod a2/d), k1 = k1 % (a2 / d), 对一个数%m等于将去整数k倍的m。

带入 x = k1a1 + m1得到:

x = (k1 + k*a2 / d)a1 + m1 = k1a1 + m1 + k*a1*a2 / d;

令a = a1*a2 /d, m = k1a1 + m1, 那么通过两个式子合并之后:x = ka + m, x ≡ m (mod a), x mod a ≡ m, 那么 x = m % a。

最后a会变成a1, a2, a3,……,an这n个数的公倍数,C++中余数可以为负,-5 % 3 = -2, 可以转化为:(-5 % 3 + 3) % 3 = 1.

 

中国剩余定理:

m1, m2, m3,……,mk两两互质。x ≡ a (mod m).

x ≡ a1 (mod m1), x ≡ a2(mod m2), x ≡ a3(mod m3),……,x ≡ ak(mod mk)

令M = m1*m2*……mk, Mi = M / mi(除了mi之外的其他所有m的乘积)所以Mi与mi互质,那么Mi * Mi^-1 ≡ 1 (mod mi),互质的两个数肯定有逆元,

Mi^-1(Mi % mi的逆元)形如 ax ≡ 1 (mod m)

通解是: x = (a1 * M1 * M1^-1 + a2M2M2^-1+……+akMkMk^-1)% m1 = a1, 因为M1*M1^-1 mod m1 = 1, 后面的所有Mi都有m1的质因子,所以%m1都为0.

 

posted @ 2020-04-18 15:15  龙雪可可  阅读(136)  评论(0编辑  收藏  举报
****************************************** 页脚Html代码 ******************************************