当前位置: 首页 > news >正文

【数论】裴蜀定理与扩展欧几里得算法 (exgcd)

文章目录

  • 一、裴蜀定理
    • 1. 裴蜀定理的内容
    • 2. 裴祖定理的推论
    • 3.【模板】裴蜀定理 ⭐⭐
  • 二、扩展欧几里得算法 (exgcd)
    • 1. exgcd 解决的问题
    • 2. exgcd 的算法过程
    • 3.【模板】二元一次不定方程 (exgcd) ⭐⭐⭐⭐
      • (1) 解题思路
      • (2) 代码实现
  • 三、exgcd 与乘法逆元
    • 1. 一次同余方程
    • 2. exgcd 求解一次同余方程
    • 3. exgcd 求解乘法逆元
    • 4. 【模板】同余方程(求逆元)⭐⭐⭐
  • 四、练习:青蛙的约会 ⭐⭐⭐
    • 1. 解题思路
    • 2. 代码实现

一、裴蜀定理

1. 裴蜀定理的内容

裴蜀定理又称贝祖定理,它的内容是:

ddd 是整数 a,ba,ba,b 的最大公约数,则一定存在整数 x,yx, yx,y,使得 ax+by=dax+by=dax+by=d

通过这个定理,我们可以判断一个不定方程是否存在整数解

注意 a,ba, ba,b 的正负是不影响结果的。因为 a,ba, ba,b 如果存在解,那么也 a,−ba, -ba,b 或者 −a,b-a, ba,b 也一定存在解,只不过是在原来解的基础上添上一个负号。


2. 裴祖定理的推论

  • ddd 是整数 a,ba,ba,b 的最大公约数,则一定存在整数 x,yx, yx,y,使得 ax+by=ndax+by=ndax+by=nd。(n∈Zn\in ZnZ

因此我们可以得出,对于一个不定方程 ax+by=cax + by = cax+by=c,如果有 gcd⁡(a,b)∣c\gcd(a, b) \mid cgcd(a,b)c,那么该不定方程一定有解。

  • 一定存在整数 x1,x2,⋯,xkx_1, x_2,\cdots, x_kx1,x2,,xk,使得方程 a1x1+a2x2+⋯+akxk=gcd⁡(a1,a2,⋯,ak)×na_1x_1 + a_2x_2 + \cdots + a_kx_k = \gcd(a_1, a_2, \cdots, a_k)\times na1x1+a2x2++akxk=gcd(a1,a2,,ak)×n 成立。

3.【模板】裴蜀定理 ⭐⭐

【题目链接】

P4549 【模板】裴蜀定理 - 洛谷

【题目描述】

给定一个包含 nnn 个元素的整数序列 AAA,记作 A1,A2,A3,...,AnA_1,A_2,A_3,...,A_nA1,A2,A3,...,An

求另一个包含 nnn 个元素的待定整数序列 XXX,记 S=∑i=1nAi×XiS=\sum\limits_{i=1}^nA_i\times X_iS=i=1nAi×Xi,使得 S>0S>0S>0SSS 尽可能的小。

【输入格式】

第一行一个整数 nnn,表示序列元素个数。

第二行 nnn 个整数,表示序列 AAA

【输出格式】

一行一个整数,表示 S>0S>0S>0 的前提下 SSS 的最小值。

【示例一】

输入

2
4059 -1782

输出

99

【说明/提示】

对于 100%100\%100% 的数据,1≤n≤201 \le n \le 201n20∣Ai∣≤105|A_i| \le 10^5Ai105,且 AAA 序列不全为 000


根据裴蜀定理, 不定方程 S∑i=1nAi×XiS\sum\limits_{i=1}^nA_i\times X_iSi=1nAi×Xi 的解为 gcd⁡(A1,A2,⋯,An)×k\gcd(A_1, A_2, \cdots, A_n)\times kgcd(A1,A2,,An)×k,要使得它的解大于零且尽可能的小,那么实际上就是求 gcd⁡(A1,A2,⋯,An)\gcd(A_1, A_2, \cdots, A_n)gcd(A1,A2,,An)

#include<iostream>using namespace std;// 欧几里得算法求最大公因数
int gcd(int a, int b)
{return b == 0 ? a : gcd(b, a % b);
}int main()
{int n; cin >> n;int ret; cin >> ret;while(--n){int x; cin >> x;ret = gcd(ret, abs(x));}cout << ret << endl;return 0;
}

二、扩展欧几里得算法 (exgcd)

1. exgcd 解决的问题

对于一个不定方程
ax+by=gcd⁡(a,b)ax+by=\gcd(a,b) ax+by=gcd(a,b)
来说,由裴祖定理我们知道这个方程一定是有解的,那么我们去求一组非零整数解 x,yx,yx,y 的过程,就要用到扩展欧几里得算法


2. exgcd 的算法过程

它的计算过程如下:

注:下面推导中得分式除法表示整除。

  • b=0b = 0b=0 时,

ax+by=axax + by = axax+by=ax,因此有一组解为 x=1,y=0x = 1, y = 0x=1,y=0

  • b≠0b \ne 0b=0 时,

由欧几里得算法得:gcd⁡(a,b)=gcd⁡(b,amodb)\gcd(a, b) = \gcd(b, a \bmod b)gcd(a,b)=gcd(b,amodb),则
gcd⁡(a,b)=ax+by⇓gcd⁡(b,amodb)=bx1+(amodb)y1=bx1+(a−ab×b)y1=ay1+b(x1−aby1)\gcd(a,b) = ax+by \\ \Downarrow \\ \begin{aligned} \gcd(b, a \bmod b) &= bx_1 + (a \bmod b)y_1 \\ &=bx_1 + (a - \frac{a}{b}\times b)y_1 \\ &= ay_1 + b(x_1 - \frac{a}{b}y_1) \end{aligned} gcd(a,b)=ax+bygcd(b,amodb)=bx1+(amodb)y1=bx1+(aba×b)y1=ay1+b(x1bay1)
等式左右应该相等,因此
x=y1,y=x1−aby1x = y1, y = x1 - \frac{a}{b}y_1 x=y1,y=x1bay1
于是我们可以利用递归,先求出下一层得 x1,y1x_1, y_1x1,y1,再求出当前的 x,yx, yx,y

上述递归过程,可以求出一组特解:x0,y0x_0, y_0x0,y0,之后我们可以构造出通解,通解为
{x=x0+bgcd⁡(a,b)×ky=y0−agcd⁡(a,b)×k\begin{cases} x = x_0 + \frac{b}{\gcd(a, b)}\times k \\ y = y_0 - \frac{a}{\gcd(a, b)}\times k \end{cases} {x=x0+gcd(a,b)b×ky=y0gcd(a,b)a×k
其中 k∈N+k\in N^+kN+

代码实现:

typedef long long LL;// exgcd(扩展欧几里得算法) 的返回值和 gcd(欧几里得算法) 一样,都是返回 gcd(a, b)
// 不同的是 exgcd 还可以求出不定方程 ax + by = gcd(a, b) 的一组特解 x, y
// 由于 C++ 只能有一个返回值,所以我们要求的 x, y 需要传引用
LL exgcd(LL a, LL b, LL& x, LL& y)
{if(b == 0){x = 1, y = 0;return a;}// b 不为 0LL x1, y1, d;// 递归求出 x1, y1d = exgcd(b, a % b, x1, y1);// 利用公式求出当前层的 x, yx = y1;y = x1 - a / b * y1;return d;
}

时间复杂度与欧几里得算法一致,为 O(log⁡n)O(\log n)O(logn)

如果自己手算一个不定方程 ax+by=gcd⁡(a,b)ax+by=\gcd(a,b)ax+by=gcd(a,b) 的解,可以利用先写出欧几里得算法的过程,之后逆向推出 xxxyyy,下面给出一个示例:

请添加图片描述


3.【模板】二元一次不定方程 (exgcd) ⭐⭐⭐⭐

【题目链接】

P5656 【模板】二元一次不定方程 (exgcd) - 洛谷

【题目描述】

给定不定方程

ax+by=cax+by=cax+by=c

若该方程无整数解,输出 −1-11
若该方程有整数解,且有正整数解,则输出其正整数解的数量,所有正整数解中 xxx 的最小值,所有正整数解中 yyy 的最小值,所有正整数解中 xxx 的最大值,以及所有正整数解中 yyy 的最大值。
若方程有整数解,但没有正整数解,你需要输出所有整数解xxx 的最小正整数值, yyy 的最小正整数值。

正整数解即为 x,yx, yx,y 均为正整数的解,0\boldsymbol{0}0 不是正整数
整数解即为 x,yx,yx,y 均为整数的解。
xxx 的最小正整数值即所有 xxx 为正整数的整数解中 xxx 的最小值,yyy 同理。

【输入格式】

第一行一个正整数 TTT,代表数据组数。

接下来 TTT 行,每行三个由空格隔开的正整数 a,b,ca, b, ca,b,c

【输出格式】

TTT 行。

若该行对应的询问无整数解,一个数字 −1-11
若该行对应的询问有整数解但无正整数解,包含 222 个由空格隔开的数字,依次代表整数解中,xxx 的最小正整数值,yyy 的最小正整数值。
否则包含 555 个由空格隔开的数字,依次代表正整数解的数量,正整数解中,xxx 的最小值,yyy 的最小值,xxx 的最大值,yyy 的最大值。

读入输出量较大,注意使用较快的读入输出方式

【示例一】

输入

7
2 11 100
3 18 6
192 608 17
19 2 60817
11 45 14
19 19 810
98 76 5432

输出

4 6 2 39 8
2 1
-1
1600 1 18 3199 30399
34 3
-1
2 12 7 50 56

【说明/提示】

对于 100%100\%100% 的数据,1≤T≤2×1051 \le T \le 2 \times {10}^51T2×1051≤a,b,c≤1091 \le a, b, c \le {10}^91a,b,c109


(1) 解题思路

求解一个二元一次不定方程 ax+by=cax + by = cax+by=c 的通解:

  1. 先利用扩展欧几里得算法求出 ax+by=gcd⁡(a,b)ax + by = \gcd(a, b)ax+by=gcd(a,b) 的一组特解 x0,y0x_0, y_0x0,y0,以及 d=gcd⁡(a,b)d = \gcd(a, b)d=gcd(a,b)

  2. 用裴蜀定理判断原方程是否有解:

    • 如果 d∤cd \nmid cdc,则无解;
    • 如果 d∣cd\mid cdc,则有解。
  3. 在有解的前提下:

    • 原方程的一组特解为

    x1=x0×cdy1=y0×cdx_1 = \frac{x_0\times c}{d}\\ y_1 = \frac{y_0\times c}{d} x1=dx0×cy1=dy0×c

    • 通解为

x=x1+bgcd⁡(a,b)×k,y=y1−agcd⁡(a,b)×kx = x_1 + \frac{b}{\gcd(a, b)}\times k,\\y = y_1 - \frac{a}{\gcd(a, b)}\times k x=x1+gcd(a,b)b×k,y=y1gcd(a,b)a×k

当我们求出特解之后,由通解的形式不难发现,当 xxx 在增大的同时,yyy 在减小,所以当 xxx 为最小正整数的时候,此时如果 yyy 不是正整数,那么 x,yx, yx,y 永远不可能同时为正整数,即没有正整数解。那么我们先利用 “模加模” 求出 xxx 的最小正整数解,

// 特解为 x, gcd(a, b) = d
int k1 = b / d,
x = (x % k1 + k1) % k1; // 把 x 补成最小正整数

之后利用原方程 ax+by=cax + by = cax+by=c 反解出 yyy,即 y=(c−ax)/by = (c - ax)/by=(cax)/b,判断 yyy 是否也为正整数即可判断原方程是否有正整数解。


(2) 代码实现

#include<iostream>using namespace std;typedef long long LL;// 扩展欧几里得求 ax + by = gcd(a, b) 的特解
LL exgcd(LL a, LL b, LL& x, LL& y)
{if(b == 0){x = 1, y = 0;return a;}LL x1, y1, d;d = exgcd(b, a % b, x1, y1);x = y1;y = x1 - a / b * y1;return d;
}int main()
{int t;scanf("%d", &t);LL a, b, c;while(t--){scanf("%lld %lld %lld", &a, &b, &c);LL x, y;LL d = exgcd(a, b, x, y);// 如果 d 不能整除 c, 则方程无解,输出-1if(c % d){printf("-1\n");continue;}// 求出 ax + by = c 的特解x = c / d * x;y = c / d * y;LL k1 = b / d, k2 = a / d;x = (x % k1 + k1) % k1;  // 把 x 补成最小正整数x = x == 0 ? k1 : x;  // 注意此时 x 有可能为 0, 不是正整数,至少要为 k1y = (c - a * x) / b;  // 根据原方程反解出 yLL max_x, max_y, min_x, min_y;if(y > 0)  // 有整数解且有正整数解{min_x = x, max_y = y;y %= k2;  // 把 y 求成最小正整数解y = y == 0 ? k2 : y;min_y = y;max_x = (c - b * y) / a;// 解的个数就是 x(或y) 的最大正整数解 - 最小正整数解 / k1(或k2) + 1printf("%lld %lld %lld %lld %lld\n", (max_x - min_x) / k1 + 1, min_x, min_y, max_x, max_y);}else  // 有整数解但没有正整数解{min_x = x;y = (y % k2 + k2) % k2;y = y == 0 ? k2 : y;min_y = y;printf("%lld %lld\n", min_x, min_y);}}return 0;
}

三、exgcd 与乘法逆元

1. 一次同余方程

形如
ax≡b(modm)ax ≡ b(\bmod m) axb(modm)
的一个方程就是同余方程,由于 xxx 的最高次数为 111,所以这是一个一次同余方程。


2. exgcd 求解一次同余方程

一个 ax≡b(modm)ax ≡ b(\bmod m)axb(modm) 这样的同余方程可以转换为 ax=ym+bax = ym + bax=ym+b,移项得 ax−my=bax - my = baxmy=b

由于正负号不影响结果,所以等价于求解 ax+my=bax + my = bax+my=b 这一个不定方程;

由裴蜀定理,当 gcd⁡(a,m)∣b\gcd(a, m)\mid bgcd(a,m)b 时,该方程有解;

所以,我们可以通过扩展欧几里得算法求出 ax+my=gcd⁡(a,m)ax + my = \gcd(a, m)ax+my=gcd(a,m) 得特解,进而判断是否有 gcd⁡(a,m)∣b\gcd(a, m)\mid bgcd(a,m)b,如果成立,那么说明 ax+my=bax + my = bax+my=b 有解,设用 exgcd 求得的 ax+my=gcd⁡(a,m)ax + my = \gcd(a, m)ax+my=gcd(a,m) 的特解为 x0,y0x_0, y_0x0,y0,则原方程的特解为
x1=x0×bgcd⁡(a,m)y1=y0×bgcd⁡(a,m)x_1 = \frac{x_0\times b}{\gcd(a, m)}\\ y_1 = \frac{y_0\times b}{\gcd(a, m)} x1=gcd(a,m)x0×by1=gcd(a,m)y0×b
进而可以求出通解。


3. exgcd 求解乘法逆元

对于正整数 aaappp,若有 ax≡1(modp)ax\equiv1\pmod pax1(modp),那么把这个同余方程中的 xxx 的解叫做 aaappp 的乘法逆元,简称逆元。

逆元可以通过费马小定理求,但是必须要求 ppp 是一个质数,但是如果 ppp 不是质数,那么我们可以用扩展欧几里得算法求解乘法逆元,不难发现,这本质上就是求解一个同余方程的过程。

原同余方程可以转化为 ax+py=1ax + py = 1ax+py=1 的形式,根据裴蜀定理,该不定方程要有解,必须满足 gcd⁡(a,p)∣1\gcd(a, p) \mid 1gcd(a,p)1,那么 gcd⁡(a,p)\gcd(a, p)gcd(a,p) 必须等于 111,也就是说 a,pa, pa,p 必须互质。所以能用扩展欧几里得求解乘法逆元的条件就是 a,pa, pa,p 互质。


4. 【模板】同余方程(求逆元)⭐⭐⭐

【题目链接】

【模板】同余方程

image-20251013124804983


#include<iostream>using namespace std;typedef long long LL;// 扩展欧几里得
LL exgcd(LL a, LL b, LL& x, LL& y)
{if(b == 0){x = 1, y = 0;return a;}LL x1, y1, d;d = exgcd(b, a % b, x1, y1);x = y1;y = x1 - a / b * y1;return d;
}int main()
{int t;cin >> t;while(t--){LL a, b;cin >> a >> b;// ax + by = 1LL x, y;int d = exgcd(a, b, x, y);// 如果 a, p 不互质,那么一定没有解if(d != 1) cout << -1 << endl;else{LL k = b / d;x = (x % k + k) % k;  // 补成最小正整数解x = x == 0 ? k : x;cout << x << endl;}}return 0;
}

四、练习:青蛙的约会 ⭐⭐⭐

【题目链接】

P1516 青蛙的约会 - 洛谷

【题目描述】

两只青蛙在网上相识了,它们聊得很开心,于是觉得很有必要见一面。它们很高兴地发现它们住在同一条纬度线上,于是它们约定各自朝西跳,直到碰面为止。可是它们出发之前忘记了一件很重要的事情,既没有问清楚对方的特征,也没有约定见面的具体位置。不过青蛙们都是很乐观的,它们觉得只要一直朝着某个方向跳下去,总能碰到对方的。但是除非这两只青蛙在同一时间跳到同一点上,不然是永远都不可能碰面的。为了帮助这两只乐观的青蛙,你被要求写一个程序来判断这两只青蛙是否能够碰面,会在什么时候碰面。

我们把这两只青蛙分别叫做青蛙 A 和青蛙 B,并且规定纬度线上东经 000 度处为原点,由东往西为正方向,单位长度 111 米,这样我们就得到了一条首尾相接的数轴。设青蛙 A 的出发点坐标是 xxx,青蛙 B 的出发点坐标是 yyy。青蛙 A 一次能跳 mmm 米,青蛙 B 一次能跳 nnn 米,两只青蛙跳一次所花费的时间相同。纬度线总长 LLL 米。现在要你求出它们跳了几次以后才会碰面。

【输入格式】

输入只包括一行五个整数 x,y,m,n,Lx,y,m,n,Lx,y,m,n,L

【输出格式】

输出碰面所需要的次数,如果永远不可能碰面则输出一行一个字符串 Impossible

【示例一】

输入

1 2 3 4 5

输出

4

【说明/提示】

对于 100%100\%100% 的数据,1≤x,y,m,n≤2×1091 \le x, y, m, n \le 2 \times 10^91x,y,m,n2×109x≠yx \ne yx=y1≤L≤2.1×1091 \le L \le 2.1 \times 10^91L2.1×109


1. 解题思路

设两个青蛙相遇时跳了 ttt 次,那么相对于原点来说,青蛙 A 跳的总路程就是 x+tmx + tmx+tm,青蛙 B 跳的总路程是 y+tny + tny+tn,且这两个总路程的差值刚好是纬度线总长度的 kkk 倍,即
x+tm−(y+tn)=klx + tm - (y + tn) = kl x+tm(y+tn)=kl
整理后得到
(m−n)t−kl=y−x(m - n)t - kl = y - x (mn)tkl=yx
这个式子其实就是一个不定方程 ax+by=cax + by = cax+by=c,我们要求的 ttt 就是这里的 xxx

上面的式子还可以看作一个同余方程,如下
(m−n)t≡y−x(modl)(m - n)t\equiv y - x\pmod l (mn)tyx(modl)
如果 m−n<0m - n < 0mn<0 那么等价于求解
(n−m)t≡x−y(modl)(n - m)t\equiv x - y\pmod l (nm)txy(modl)


2. 代码实现

#include<iostream>using namespace std;typedef long long LL;LL exgcd(LL a, LL b, LL& x, LL& y)
{if(b == 0){x = 1, y = 0;return a;}LL x1, y1, d;d = exgcd(b, a % b, x1, y1);x = y1;y = x1 - a / b * y1;return d;
}int main()
{LL x, y, m, n, l;cin >> x >> y >> m >> n >> l;// 设跳 x0 次LL a = m - n, b = l, c = y - x, x0, y0;if(a < 0)  // 处理负数{a = -a;c = -c;}// a * x0 + b * y0 = cLL d = exgcd(a, b, x0, y0);if(c % d) cout << "Impossible" << endl;else{x0 = c / d * x0;LL k = b / d;x0 = (x0 % k + k) % k;cout << x0 << endl;}return 0;
}
http://www.dtcms.com/a/569055.html

相关文章:

  • 新北区城乡建设局网站wordpress 瑜伽课程
  • 解读ffmpeg控制台输出
  • SQL Server 集群网络架构详解
  • php做视频网站源码上海建设网站公
  • 外国网站上做Taskvs2012网站开发环境
  • STM32的串口(上位机控制下位机)
  • Anthropic 经济指数报告:地理和企业人工智能采用的不均衡性
  • 美工免费素材网站淮南网格员招聘
  • 坐标系转换快速定位问题
  • ELK学习笔记
  • 2025世界职业院校技能大赛:10分钟展演黄金时间分配
  • rust表格文件处理
  • 基于VU13P的6U VPX 载板
  • 做网站的害处道滘仿做网站
  • 128天写作之旅:记录与成长的点滴
  • 个人理财系统|基于java+小程序+APP的个人理财系统设计与实现(源码+数据库+文档)
  • 用什么做php网站十大货源网站大全
  • 广州网站制作哪家全面电脑做网站用word
  • 蓝牙钥匙 第40次 汽车租赁与试驾场景中的智能权限管理:关键技术与实践方案
  • 李沐深度学习笔记D1-什么是深度学习
  • 做全景的网站对网站策划的看法
  • 浅谈balance_pgdat函数的工作原理和作用
  • 假设A调用B,B调用C,只有C方法涉及到多张表的增删改操作,那么@Transactional注解添加在A方法上,和添加在C方法上,哪个性能更好?
  • 班级网站设计模板首页中国建设银行网站地图
  • 全国产化方案实现NVMe over 100G RDMA,解决智算超算中“存算”不匹配问题
  • 营业执照识别接口开发者集成指南-OCR API
  • 中国建设工程质量协会网站建电子商务网站需要多少钱
  • 使用Worker打包报错
  • 04.LLM主流开源代表模型
  • k8s问答题(二)