扩展欧几里得算法(C++)

扩展欧几里得算法

前置知识:裴蜀定理

裴蜀定理

\qquad a , b a,b a,b 是整数,且 ( a , b ) = d (a,b)=d (a,b)=d,那么对于任意的整数 x , y , a x + b y x,y,ax+by x,y,ax+by 都一定是 d d d 的倍数。特别地,一定存在整数 x , y x,y x,y,使 a x + b y = d ax+by=d ax+by=d 成立,(x,y不唯一)。

\qquad x 0 , y 0 x_0,y_0 x0,y0 满足 a x + b y = d ax+by=d ax+by=d,则满足 { x = x 0 − b d ⋅ k y = y 0 + a d ⋅ k , k ∈ Z \begin{cases}x=x_0-\frac{b}{d}\cdot k \\ y=y_0+\frac{a}{d}\cdot k\end{cases},k\in \mathbb Z {x=x0dbky=y0+dak,kZ,的 x x x y y y 也均满足上式。且这些是 x , y x,y x,y 所有可能的解。

作用

\qquad 扩展欧几里得算法就是用来求解这个特殊的 x , y x,y x,y.

推导证明

a    m o d    b = a − ⌊ a b ⌋ ⋅ b d = ( a , b ) = ( b , a    m o d    b ) = ( a − ⌊ a b ⌋ ⋅ b ) x + b y d = a x + b ( y − ⌊ a b ⌋ x ) a\;mod\;b=a-\lfloor \frac{a}{b} \rfloor \cdot b \\ d=(a,b)=(b,a\;mod\;b)=\left(a-\lfloor \frac{a}{b} \rfloor \cdot b\right)x+by \\ d=ax+b\left(y-\lfloor \frac{a}{b} \rfloor x\right) amodb=ababd=(a,b)=(b,amodb)=(abab)x+byd=ax+b(ybax)

\qquad 这里的 y − ⌊ a b ⌋ x y-\lfloor \frac{a}{b} \rfloor x ybax 就是下面代码第9行 y -= a / b * x; 的由来。

代码

#include <iostream>
using namespace std;
int exgcd(int a, int b, int &x, int &y) {		//以传址的方式返回两个参数
	if (!b) {
		x = 1, y = 0;
		return a;
	}
	int d = exgcd(b, a % b, y, x);
	y -= a / b * x;
	return d;
}
int main() {
	int a, b, x, y;
	scanf("%d%d", &a, &b);
	exgcd(a, b, x, y);
	printf("%d %d\n", x, y);
	return 0;
}

应用

1.求逆元

前置知识:逆元

\qquad 若整数 b b b m m m 互质,并且对于任意的整数 a a a,如果满足 b ∣ a b|a ba,则存在一个整数 x x x,使得 a b ≡ a × x ( m o d   m ) \frac{a}{b}\equiv a\times x(mod\space m) baa×x(mod m),则称 x x x b b b 的模 m m m 乘法逆元,记为 b − 1 ( m o d   m ) b^{−1}(mod\space m) b1(mod m)

也就是说,求得一个 x x x,使得 b ⋅ x ≡ 1 ( m o d   m ) b\cdot x\equiv 1(mod\space m) bx1(mod m) 成立。

思路

\qquad 这个问题可以看作求一元线性同余方程的一种特殊情况。
∃ x ∈ Z , 使得    a x ≡ 1 ( m o d    m )    成立 ⇕ ∃ x , y ∈ Z , 使得    a x = m y + 1    成立 ⇕ 令 y ′ = − y ,    ∃ x , y ′ ∈ Z , 使得    a x + m y ′ = 1    成立 \exist x\in \mathbb{Z},使得\;ax\equiv 1(mod \; m)\;成立 \\ \Updownarrow \\ \exist x,y\in \mathbb{Z},使得\;ax=my+1 \;成立 \\ \Updownarrow \\ 令y'=-y,\; \exist x,y'\in \mathbb{Z},使得\;ax+my'=1 \;成立 xZ,使得ax1(modm)成立x,yZ,使得ax=my+1成立y=y,x,yZ,使得ax+my=1成立
\qquad 根据裴蜀定理可知,当且仅当 ( a , m ) = 1 (a,m)=1 (a,m)=1,即 a a a m m m 互质时,此线性同余方程有解,即 a a a 有存在逆元。

\qquad 利用扩展欧几里得算法求得的 x x x 即为 a a a 的逆元。

代码
#include <iostream>
using namespace std;
int exgcd(int a, int b, int &x, int &y) {		//以传址的方式返回两个参数
	if (!b) {
		x = 1, y = 0;
		return a;
	}
	int d = exgcd(b, a % b, y, x);
	y -= a / b * x;
	return d;
}
int main() {
	int a, m, x, y;
	scanf("%d%d", &a, &m);
	int d = exgcd(a, m, x, y);
	if (d == 1)
		printf("%d\n", x < 0 ? x + m : x);		//为了防止出现负数,等价于:printf("%d\n", (x + m) % m);
	else
		puts("no exist");
	return 0;
}

2.求解线性同余方程

例题

878. 线性同余方程 - AcWing题库

\qquad 给定 n n n 组数据 a i , b i , m i a_i,b_i,m_i ai,bi,mi,对于每组数求出一个 x x x ,使其满足 a i ∗ x i ≡ b i ( m o d    m i ) a_i*x_i\equiv b_i(mod \; m_i) aixibi(modmi),如果无解,则输出 impossible.

数据范围

\qquad 1 ≤ n ≤ 1 0 5 , 1 ≤ a i , b i , m i ≤ 2 × 1 0 9 1 \le n \le 10^5,\quad1\le a_i,b_i,m_i\le 2\times 10^9 1n105,1ai,bi,mi2×109

输入样例

2
2 3 6
4 3 5

输出样例

impossible
2
思路

\qquad 这就是上文求逆元方法的扩展和延申。
∃ x ∈ Z , 使得    a x ≡ b ( m o d    m )    成立 ⇕ ∃ x , y ∈ Z , 使得    a x = m y + b    成立 ⇕ 令 y ′ = − y ,    ∃ x , y ′ ∈ Z , 使得    a x + m y ′ = b    成立 \exist x\in \mathbb{Z},使得\;ax\equiv b(mod \; m)\;成立 \\ \Updownarrow \\ \exist x,y\in \mathbb{Z},使得\;ax=my+b \;成立 \\ \Updownarrow \\ 令y'=-y,\; \exist x,y'\in \mathbb{Z},使得\;ax+my'=b \;成立 xZ,使得axb(modm)成立x,yZ,使得ax=my+b成立y=y,x,yZ,使得ax+my=b成立
\qquad 根据裴蜀定理可知,记 ( a , m ) = d (a,m)=d (a,m)=d 当且仅当 d    ∣    b d\;|\;b db 时,此线性同余方程有解。

\qquad 要求 x x x ,则需用扩展欧几里得算法算出满足 a x 0 + m y 0 = d ax_0+my_0=d ax0+my0=d 式子的 x 0 x_0 x0,可得 x = x 0 ⋅ b d x=x_0\cdot\frac{b}{d} x=x0db .

举个例子:

要解 4 x ≡ 3 ( m o d    5 ) 4x\equiv 3(mod\;5) 4x3(mod5) 这一方程:

  1. 先判断 ( 4 , 5 ) = 1 (4,5)=1 (4,5)=1 3 3 3 的因数,说明有解;
  2. 再找出满足 4 x 0 + 5 y 0 = 1 4x_0+5y_0=1 4x0+5y0=1 的一组解 x 0 = − 1 ,    y 0 = 1 x_0=-1,\;y_0=1 x0=1,y0=1.
  3. 最后计算 x x x − 1 × 3 1 = − 3 -1\times\frac{3}{1}=-3 1×13=3,因此解为 x ≡ − 3 ( m o d    5 ) x\equiv-3(mod\;5) x3(mod5).
代码
#include <iostream>
using namespace std;
typedef long long LL;
int exgcd(int a, int b, int &x, int &y) {
	if (!b) {
		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, x, y;
		scanf("%d%d%d", &a, &b, &m);
		int d = exgcd(a, m, x, y);
		if (b % d)
			puts("impossible");
		else{
            LL res = (LL)x * b / d % m;
            printf("%lld\n", res < 0 ? res + m : res);
		}
	}
	return 0;
}

3.求解线性同余方程组

前置知识:中国剩余定理

\qquad 中国剩余定理又称孙子定理或中国余数定理,是数论中的一个关于一元线性同余方程组的定理,说明了一元线性同余方程组有解的准则以及求解方法。

经典题目表述

有物不知其数,三三数之剩二,五五数之剩三,七七数之剩二。问物几何?

\qquad 大意是:一个整数除以三余二,除以五余三,除以七余二,求这个整数。

\qquad 现代数学的语言来说明,题目为:给出以下一元线性同余方程组,求满足下列同余式的整数 x x x.
{ x ≡ a 1 ( m o d    m 1 ) x ≡ a 2 ( m o d    m 2 ) ⋮ x ≡ a n ( m o d    m n ) \begin{cases} x\equiv a_1(mod\;m_1) \\ x\equiv a_2(mod\;m_2) \\ \vdots \\ x\equiv a_n(mod\;m_n) \\ \end{cases} xa1(modm1)xa2(modm2)xan(modmn)
其中, m 1 , m 2 , … m n m_1,m_2,\dots m_n m1,m2,mn 两两互质

注意:这里两两互质是指任意两个数均互质,而不是这些数的最大公因数为1.

例如:{6,8,9} 这三个数的最大公因数为1,但是他们并非两两互质。

解题方法
设 M = ∏ i = 1 n m i , M i = M m i 用 M i − 1 表示 M i 在模 m i 意义下的逆元,则解为: x ≡ ∑ i = 1 n a i M i M i − 1    ( m o d    M ) 设M=\prod\limits_{i=1}^{n}m_i,\quad M_i=\frac{M}{m_i} \\ 用M_i^{-1}表示M_i在模m_i意义下的逆元,则解为: \\ x\equiv \sum\limits_{i=1}^{n}a_iM_iM_i^{-1}\;(mod\;M) M=i=1nmi,Mi=miMMi1表示Mi在模mi意义下的逆元,则解为:xi=1naiMiMi1(modM)
\qquad 在此处需要用扩展欧几里得定理计算一个数字的逆元。由此我们就可以知道,这种方法需要 m 1 , m 2 , … m n m_1,m_2,\dots m_n m1,m2,mn 两两互质的原因就在于需要求每个 M i M_i Mi 在模 m i m_i mi 下的逆元(逆元存在的充要条件就是 M i M_i Mi m i m_i mi 互质)。

举个例子:
{ x ≡ 2 ( m o d    3 ) x ≡ 3 ( m o d    5 ) x ≡ 2 ( m o d    7 ) \begin{cases} x\equiv 2(mod\;3) \\ x\equiv 3(mod\;5) \\ x\equiv 2(mod\;7) \\ \end{cases} x2(mod3)x3(mod5)x2(mod7)
就拿“物不知数”这个经典题举例:

  1. 首先判断 3 , 5 , 7 3,5,7 3,5,7 之间确实两两互质,可以使用中国剩余定理;

  2. 求出 M = 3 × 5 × 7 = 105 M=3\times5\times7=105 M=3×5×7=105

  3. 分别计算 M i M_i Mi M i − 1 M_i^{-1} Mi1
    M 1 = 5 × 7 = 35 ,    M 1 − 1 = 2 − 1 ( m o d    3 ) = 2 ; M 2 = 3 × 7 = 21 ,    M 2 − 1 = 1 − 1 ( m o d    5 ) = 1 ; M 3 = 3 × 5 = 15 ,    M 3 − 1 = 1 − 1 ( m o d    7 ) = 1 ; M_1=5\times7=35,\;M_1^{-1}=2^{-1}(mod\;3)=2; \\ M_2=3\times7=21,\;M_2^{-1}=1^{-1}(mod\;5)=1; \\ M_3=3\times5=15,\;M_3^{-1}=1^{-1}(mod\;7)=1; \\ M1=5×7=35,M11=21(mod3)=2;M2=3×7=21,M21=11(mod5)=1;M3=3×5=15,M31=11(mod7)=1;

  4. 求解 x x x.
    x = a 1 M 1 M 1 − 1 + a 2 M 2 M 2 − 1 + a 3 M 3 M 3 − 1 = 2 × 35 × 2 + 3 × 21 × 1 + 2 × 15 × 1 = 233 因此, x ≡ 23 ( m o d    105 ) x=a_1M_1M_1^{-1}+a_2M_2M_2^{-1}+a_3M_3M_3^{-1} \\ =2\times35\times2+3\times21\times1+2\times15\times1 \\ =233\\ 因此,x\equiv 23 (mod\;105) x=a1M1M11+a2M2M21+a3M3M31=2×35×2+3×21×1+2×15×1=233因此,x23(mod105)
    最小的正整数解为 23 23 23

注意:

\qquad 这个定理给出了在 m 1 , m 2 , … m n m_1,m_2,\dots m_n m1,m2,mn 两两互质这种特殊情况下的构造解,而并不是一元线性同余方程组的通解。

\qquad 我们可以验证这个定理的正确性

\qquad 只需验证
对于 ∀ i ∈ { x ∈ N ∗ ∣ 1 ≤ x ≤ n } , 均满足 ∑ j = 1 n a j M j M j − 1 ≡ a i ( m o d    m i ) 对于\forall i \in \{x\in \mathbb{N}^*|1\le x \le n\},均满足\sum\limits_{j=1}^{n}a_jM_jM_j^{-1} \equiv a_i(mod\;m_i) 对于i{xN∣1xn},均满足j=1najMjMj1ai(modmi)
\qquad 计算 ∑ j = 1 n a j M j M j − 1 \sum\limits_{j=1}^{n}a_jM_jM_j^{-1} j=1najMjMj1 的值时,可以看作这个式子中 n n n 项分别取模再相加。对于其中某一项:

  1. j ≠ i j\ne i j=i 时,其 M j M_j Mj 中包含因数 m i m_i mi,因此该项对 m i m_i mi 取模得 0 0 0.
  2. j = i j= i j=i 时,其 M j ⋅ M j − 1 M_j\cdot M_j^{-1} MjMj1 m i m_i mi 取模得 1 1 1,故实际为 a i a_i ai m i m_i mi 取模。

综上所述,
∑ j = 1 n a j M j M j − 1 ( m o d    m i ) = a i + 0 + 0 + ⋯ + 0 = a i \sum\limits_{j=1}^{n}a_jM_jM_j^{-1}(mod\;m_i)=a_i+0+0+\cdots+0=a_i j=1najMjMj1(modmi)=ai+0+0++0=ai

代码:
#include <iostream>
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;
}
LL M = 1, m1[30], m2[30];
LL a[30], m[30], res;
int main() {
	int n;
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) {
		scanf("%lld%lld", a + i, m + i);
		M *= m[i];
	}
	for (int i = 1; i <= n; i++) {
		m1[i] = M / m[i];
		LL x, y;
		LL d = exgcd(m1[i], m[i], x, y);
		if (d == 1) {
			m2[i] = x < 0 ? x + m[i] : x;
		} else {				//说明不满足两两互质
			puts("error!");
			return 0;
		}
		res += a[i] * m1[i] * m2[i] % M;
	}
	res %= M;
	printf("%lld", res < 0 ? res + M : res);
	return 0;
}
例题

204. 表达整数的奇怪方式 - AcWing题库(略微改动)

\qquad 给定 2 n 2n 2n 个整数 a 1 , a 2 , … , a n a_1,a_2,\dots,a_n a1,a2,,an m 1 , m 2 , … , m n m_1,m_2,\dots,m_n m1,m2,,mn,求一个最小的非负整数 x x x,满足 ∀ i ∈ [ 1 , n ] , x ≡ a i ( m o d    m i ) \forall i \in [1,n],x\equiv a_i(mod\;m_i) i[1,n],xai(modmi).

数据范围

\qquad 1 ≤ a i ≤ 2 31 − 1 , 1\le a_i\le2^{31}-1, 1ai2311,

\qquad 0 ≤ a i < m i 0\le a_i <m_i 0ai<mi

\qquad 1 ≤ n ≤ 25 1\le n\le 25 1n25

输入样例

2
7 8			//a1和m1
9 11		//a2和m2

输出样例

31
思路

\qquad 此题没有指明 a 1 , a 2 , … a n a_1,a_2,\dots a_n a1,a2,an 两两互质,因此不能盲目使用中国剩余定理

\qquad 采用另一种通解,步骤如下:
{ x ≡ a 1 ( m o d    m 1 ) ( 1 ) x ≡ a 2 ( m o d    m 2 ) ( 2 ) ⋮ x ≡ a n ( m o d    m n ) \begin{cases} x\equiv a_1(mod\;m_1)\qquad(1) \\ x\equiv a_2(mod\;m_2)\qquad(2) \\ \vdots \\ x\equiv a_n(mod\;m_n) \\ \end{cases} xa1(modm1)(1)xa2(modm2)(2)xan(modmn)
\qquad 先研究(1)(2)式组成的方程组,可以写作:
x = k 1 m 1 + a 1    ( k 1 ∈ Z ) = k 2 m 2 + a 2    ( k 2 ∈ Z ) x=k_1m_1+a_1\;(k_1\in\Bbb{Z}) \\ \quad=k_2m_2+a_2\;(k_2\in\Bbb{Z}) x=k1m1+a1(k1Z)=k2m2+a2(k2Z)
\qquad 变形为:
k 1 m 1 − k 2 m 2 = a 2 − a 1 ( 3 ) k_1m_1-k_2m_2=a_2-a_1\qquad\qquad(3) k1m1k2m2=a2a1(3)
\qquad 根据裴蜀定理,要使 k 1 , k 2 k_1,k_2 k1,k2 有解,即 ( m 1 , m 2 ) ∣ ( a 2 − a 1 ) (m_1,m_2)|(a_2-a_1) (m1,m2)(a2a1).

\qquad d = ( m 1 , m 2 ) d=(m_1,m_2) d=(m1,m2) ,利用扩展欧几里得算法可以得到 k 1 m 1 − k 2 m 2 = d k_1m_1-k_2m_2=d k1m1k2m2=d 的一组解,记作 k 1 ′ , k 2 ′ k_1',k_2' k1,k2。将其扩大至成为(3)式的解:
{ k 10 = k 1 ′ ⋅ a 2 − a 1 d k 20 = k 2 ′ ⋅ a 2 − a 1 d \begin{cases} k_{10}=k_1'\cdot \frac{a_2-a_1}{d} \\ k_{20}=k_2'\cdot \frac{a_2-a_1}{d} \end{cases} {k10=k1da2a1k20=k2da2a1
\qquad 那么所有的解为 { k 1 = k 10 + m 2 d ⋅ p k 2 = k 20 + m 1 d ⋅ p , p ∈ Z \begin{cases}k_1=k_{10}+\frac{m_2}{d}\cdot p \\ k_2=k_{20}+\frac{m_1}{d}\cdot p\end{cases},p\in \mathbb Z {k1=k10+dm2pk2=k20+dm1p,pZ,则
x = k 1 m 1 + a 1    = ( k 10 + m 2 d ⋅ p ) m 1 + a 1 = m 1 k 10 + a 1 + p ⋅ m 1 m 2 d = m 1 k 10 + a 1 + p ⋅ [ m 1 , m 2 ] x=k_1m_1+a_1\qquad\qquad\qquad\; \\ =\left(k_{10}+\frac{m_2}{d}\cdot p\right)m_1+a_1\\ =m_1k_{10}+a_1+p\cdot\frac{m_1m_2}{d} \\ =m_1k_{10}+a_1+p\cdot[m_1,m_2] x=k1m1+a1=(k10+dm2p)m1+a1=m1k10+a1+pdm1m2=m1k10+a1+p[m1,m2]
\qquad 中括号表示两个数的最小公倍数。

\qquad 这样来看,我们把两个式子融合成了一个形式相同的式子:
令 M = [ m 1 , m 2 ] ,    A = m 1 k 10 + a 1 则 : x = p ⋅ M + A 即 : x ≡ A ( m o d    M ) 令M=[m_1,m_2],\;A=m_1k_{10}+a_1 \\ 则: x=p\cdot M+A\\ 即: x\equiv A(mod\;M) M=[m1,m2],A=m1k10+a1:x=pM+A:xA(modM)

代码
#include <iostream>
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;
	scanf("%d", &n);

	bool flag = true;		//是否有解
	LL a1, m1;

	scanf("%lld%lld", &a1, &m1);

	for (int i = 1; i < n; i++) {
		LL a2, m2;
		scanf("%lld%lld", &a2, &m2);

		LL k1, k2;
		LL d = exgcd(m1, m2, k1, k2);
		if ((a2 - a1) % d) {
			flag = false;		//无解
			break;
		}

		k1 *= (a2 - a1) / d;
		LL t = m2 / d;
		k1 = (k1 % t + t) % t;		//更新k1为符合要求的最小正整数

		a1 = m1 * k1 + a1;			//转化成一个新的式子
		m1 = m1 / d * m2;
	}
	if (flag)
		printf("%lld\n", (a1 % m1 + m1) % m1);
	else
		puts("-1");
	return 0;
}
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
扩展欧几里得算法是求解一元一次不定方程 ax + by = gcd(a,b) 的一种方法,其中 a 和 b 是整数,gcd(a,b) 是它们的最大公约数,x 和 y 是整数解。逆元是指在模运算下,一个数的乘法逆元是指与它相乘后模运算得到 1 的数。在数论中,常常需要求一个数在模意义下的逆元,即一个数 k 满足 (k * x) % m = 1,其中 m 是模数。 下面是扩展欧几里得算法求逆元的 C 语言实现: ```c #include <stdio.h> // 扩展欧几里得算法 int exgcd(int a, int b, int *x, int *y) { if (b == 0) { *x = 1; *y = 0; return a; } int gcd = exgcd(b, a % b, y, x); *y -= a / b * (*x); return gcd; } // 求逆元 int modinv(int a, int m) { int x, y; int gcd = exgcd(a, m, &x, &y); if (gcd != 1) { return -1; // a 和 m 不互质,不存在逆元 } else { return (x % m + m) % m; // 转化为正整数 } } int main() { int a = 3, m = 11; int inv = modinv(a, m); if (inv == -1) { printf("%d 在模 %d 意义下不存在逆元\n", a, m); } else { printf("%d 在模 %d 意义下的逆元是 %d\n", a, m, inv); } return 0; } ``` 这个程序中,exgcd 函数通过递归实现扩展欧几里得算法,返回 a 和 b 的最大公约数,并且求出 x 和 y 的值。在 modinv 函数中,我们调用 exgcd 函数求出 a 和 m 的最大公约数,并且判断 a 和 m 是否互质,如果不互质则不存在逆元。否则,根据扩展欧几里得算法的结果,求出 x 的值作为 a 在模 m 意义下的逆元。注意,由于 x 可能是负数,所以要将其转化为正整数。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值