扩展欧几里得算法,费马小定理,乘法逆元,同余,裴蜀定理,基础数论内容


一、同余式

如果两整数a,b模m的余数相同,则称a,b模m同余,记为a ≡ b(mod m)。如:8 ≡ 2(mod 3)


二、乘法逆元

如果a、b互质,且满足同余方程ax ≡ 1 (mod b),则称x为a模b的乘法逆元,记作a^-1

如:8x ≡ 1(mod 5),解得x = 2,7,12……


三、费马小定理

若p为质数,且a,p互质,则a^(p-1) ≡ 1(mod p),如:4^(3-1) ≡ 1(mod 3)

证明:构造一个与p互质的序列A = { 1, 2, 3, … , p - 1 }。

现在来证明:∏a*Ai ≡ ∏Ai (mod p),i∈[1, p - 1],每个a * Ai(mod p)必独一无二

假设aAi(mod p)与aAj(mod p)的余数都是r,则aAi = xp + r,aAj = yp + r => a(Ai - Aj) = (x - y)p

左边不是p的倍数,而右边是p的倍数,矛盾

从而∏a*Ai mod p就对应了p - 1个不同的余数即A = { 1, 2, 3, … , p - 1 },则∏a*Ai ≡ ∏Ai (mod p)
继而有 a^(p-1) ≡ 1(mod p)


四、裴蜀定理

裴蜀定理又称(贝祖定理)

一定存在整数x,y,满足ax + by = gcd(a, b)。

如:4x + 6y = 2,有整数解x = -1,y = 1,而4x + 6y = 3,x = (3 - 6y) / 4无整数解

证明:

设取整数x0,y0时,ax + by的最小正整数为s,即ax0 + by0 = s,因gcd(a,b) | ax0,gcd(a,b) | by0,故gcd(a,b) | s……(1)

a = qs + r(0 ≤ r < s)

r = a - qs = a - q(ax0 + by0) = a(1 - qx0) + b(-qy0),ax + by的最小正整数为s,故r = 0

则s | a,同理s | b,则s | gcd(a,b)……(2)

故s = gcd(a,b)

4.1裴蜀定理推广一

一定存在整数x,y,满足ax + by = gcd(a,b) × n

如:4x + 6y = 8,有整数解x = -4,y = 4

4.2裴蜀定理推广二

一定存在整数X1……Xi,满足ΣAiXi = gcd(A1,A2……An)
如:4x1 + 6x2 + 2x3 = 2,有整数解x1 = 0,x2 = 0,x3 = 1。

注意
欧几里得算法求gcd(a,b),如果代入负数,结果会返回负数
例: gcd(8,-4) = -4
如果系数Ai为负数,求gcd时可代入其绝对值,确保所求最大公约数为正数,这样并不会影响解的存在。
P4549 【模板】裴蜀定理 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

#include <iostream>
#include <algorithm>
#include <cmath>

using namespace std;
#define int long long
int n, ans;
int gcd(int a, int b)
{
    return !b ? a : gcd(b, a % b);
}
signed main()
{
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin >> n;
    for (int i = 0, a; i < n; i++)
    {
        cin >> a, ans = gcd(ans, abs(a));
    }
    cout << ans;
    return 0;
}

五、扩展欧几里得算法

问题:求ax + by = gcd(a,b)的一组整数解

  • 当b = 0,显然有x = 1,y = 0,这和我们欧几里得算法递归终止条件相同,也是扩欧递归有解的原因
  • 当b ≠ 0,由欧几里得算法可知, gcd(a,b) = gcd(b,a%b)
    • 由裴蜀定理,gcd(a,b)= ax + by
      gcd(b, a%b) = bx1 + (a%b)y1
      = bx1 + (a%b)y1
      = bx1 + (a - [a/b]*b)y1
      = ay1 + b(x1 - (a/b) * y1)
      => x = y1,y = x1 - (a/b) * y1
    • 可以用递归算法,先求出下一层的x1,y1,再回代到上一层,层层回代,可求特解(x0,y0)
    • 于是有通解:x = x0 + b / gcd(a, b) * k,y = y0 - b / gcd(a, b) * k

5.1扩欧求二元一次不定方程

问题:求ax + by = c的一组整数解

如果gcd(a, b) | c,那么我们可以用扩欧求出x0,y0,进而求出该方程的解

如果gcd(a, b) ∤ c,那么无解

5.11OJ练习

P5656 【模板】二元一次不定方程 (exgcd) - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

跑板子即可

这道题额外要求处理xy的最大最小值,注意细节

#include <iostream>
#include <algorithm>
#include <cmath>
#include <queue>

using namespace std;
#define int long long
int read()
{
    int s = 0, w = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9')
    {
        if (ch == '-')
            w *= -1;
        ch = getchar();
    }

    while (ch >= '0' && ch <= '9')
    {
        s = (s << 1) + (s << 3) + (ch ^ 48);
        ch = getchar();
    }
    return s * w;
}
void write(int x)
{
    if (x < 0)
        x = -x, putchar('-');
    if (x > 9)
        write(x / 10);
    putchar((x % 10) ^ 48);
}
int gcd(int a, int b)
{
    return !b ? a : gcd(b, a % b);
}
int exgcd(int a, int b, int &x, int &y)
{
    if (!b)
    {
        x = 1, y = 0;
        return a;
    }
    int x1, y1, d;
    d = exgcd(b, a % b, x1, y1);
    x = y1, y = x1 - (a / b) * y1;
    return d;
}
signed main()
{
    freopen("in.txt", "r", stdin);
    // ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int _ = read(), a, b, c, d, x, y, k;
    while (_--)
    {
        a = read(), b = read(), c = read(), d = exgcd(a, b, x, y);

        if (c % d)
            write(-1);
        else
        {
            x *= c / d, y *= c / d, a /= d, b /= d;
            if (x < 0)
                k = ceil((1.0 - x) / b), x += k * b, y -= k * a;
            else if (x >= 0)
                k = (x - 1) / b, x -= b * k, y += a * k;
            if (y > 0)
                write((y - 1) / a + 1), putchar(' '), write(x), putchar(' '), write((y - 1) % a + 1), putchar(' '), write(x + (y - 1) / a * b), putchar(' '), write(y);
            else
                write(x), putchar(' '), write(y + a * ceil(1.0 - y / a));
        }
        putchar('\n');
    }
    return 0;
}

5.2扩欧求同余方程

给定整数a,b,m,关于x 的同余方程 ax ≡ b(mod m)

如果 x 存在整数解,则输出任意一个
如果不存在,则输出none

我们对同余方程进行转换:ax + my = b

然后进行扩展欧几里得算法即可求出ax + my = gcd(a, m)的特解(注意d ∤ b时无解)

然后x * b / d即为原方程特解

5.21OJ练习

[P1082 NOIP2012 提高组] 同余方程 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

跑板子即可,注意结果可能为负数,所以得加上b再mod b

#include <iostream>
#include <algorithm>
#include <cmath>
#include <queue>

using namespace std;
#define int long long
int read()
{
    int s = 0, w = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9')
    {
        if (ch == '-')
            w *= -1;
        ch = getchar();
    }

    while (ch >= '0' && ch <= '9')
    {
        s = (s << 1) + (s << 3) + (ch ^ 48);
        ch = getchar();
    }
    return s * w;
}
void write(int x)
{
    if (x < 0)
        x = -x, putchar('-');
    if (x > 9)
        write(x / 10);
    putchar((x % 10) ^ 48);
}
int gcd(int a, int b)
{
    return !b ? a : gcd(b, a % b);
}
int exgcd(int a, int b, int &x, int &y)
{
    if (!b)
    {
        x = 1, y = 0;
        return a;
    }
    int x1, y1, d;
    d = exgcd(b, a % b, x1, y1);
    x = y1, y = x1 - (a / b) * y1;
    return d;
}
signed main()
{
    // freopen("in.txt", "r", stdin);
    // ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int a = read(), b = read(), x, y;
    int d = exgcd(a, b, x, y);
    // ax+by =1

    write((x + b) % b);

    return 0;
}
  • 25
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

EQUINOX1

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值