裴蜀定理 扩展欧几里得算法 中国剩余定理

目录

  • 裴蜀定理
    • 裴蜀定理的定义
    • 裴蜀定理求解二元不定方程
  • 扩展欧几里得算法
    • 算法的简介
    • 算法的应用场景
    • 算法实现过程的证明
    • 解不定线性方程组
      • 代码实现
    • 线性同余方程
      • 代码实现
  • 中国剩余定理
    • 中国剩余定理的定义
    • 表达整数的奇怪方式(高难)
      • 实现思路
      • 代码实现


裴蜀定理

裴蜀定理的定义

a , b a,b a,b 是正整数,且 g c d ( a , b ) = d gcd(a,b)=d gcd(a,b)=d,那么:

  • 对于任意的整数 x , y , a x + b y x,y,ax+by x,yax+by 都一定是 d d d 的倍数。
  • g c d ( a , b ) gcd(a,b) gcd(a,b) a x + b y ax + by ax+by 能凑出来的最小正整数。
  • 一定存在整数 x , y x,y x,y (不唯一),使 a x + b y = d ax+by=d ax+by=d 成立。

什么是贝祖数?

例如: 2 x + y = 3 2x+y=3 2x+y=3,那么符合这个等式的 { x = 1 y = 1 \left\{\begin{array}{l}x = 1 \\ y = 1\end{array}\right. {x=1y=1 就是一组贝祖数。


裴蜀定理求解二元不定方程

设二元一次不定方程

a x + b y = c ax+by=c ax+by=c

(其中 a , b , c a, b, c a,b,c 是整数,且 a , b a, b a,b 都不是 0 0 0)

假定有一组整数解 { x = x 0 y = y 0 \left\{\begin{array}{l}x = x_0 \\ y = y_0\end{array}\right. {x=x0y=y0,则原式的一切整数的通解可以表示成:

{ x = x 0 − b ( a , b ) t y = y 0 + a ( a , b ) t ( t ∈ Z ) \begin{cases} x=x_0- \frac b{(a,b)}t \\y=y_0+ \frac a{(a,b)}t\qquad (t\in\mathbb{Z})\end{cases} {x=x0(a,b)bty=y0+(a,b)at(tZ)

其中 ( a , b ) (a,b) (a,b) 代表 a a a b b b 的最大公约数。

证明过程:

x 0 , y 0 x_0,y_0 x0,y0 是方程(1)的一个整数解组,即:
a x 0 + b y 0 = c ( 1 ) ax_0+by_0=c\qquad (1) ax0+by0=c(1)

x 0 , y 0 x_0,y_0 x0,y0 代入方程(1),可得:
a ( x − x 0 ) + b ( y − y 0 ) = 0 a(x-x_0) + b(y-y_0) = 0 a(xx0)+b(yy0)=0

因为 a , b a,b a,b 都是非0整数,所以上式等价于:
a ( a , b ) ( x − x 0 ) + b ( a , b ) ( y − y 0 ) = 0 \frac{a}{(a,b)}(x-x_0)+\frac{b}{(a,b)}(y-y_0)=0 (a,b)a(xx0)+(a,b)b(yy0)=0

上式为:
− b ( a , b ) ( y − y 0 ) = a ( a , b ) ( x − x 0 ) -\frac{b}{(a,b)}(y-y_0) = \frac{a}{(a,b)}(x-x_0) (a,b)b(yy0)=(a,b)a(xx0)

因为:
( b ( a , b ) , a ( a , b ) ) = 1 (\frac{b}{(a,b)},\frac{a}{(a,b)})=1 ((a,b)b,(a,b)a)=1

所以:
a ( a , b ) ∣ ( y − y 0 ) \frac{a}{(a,b)}|(y-y_0) (a,b)a(yy0)
y = y 0 + a ( a , b ) t ( t ∈ Z ) y=y_0+\frac{a}{(a,b)}t\qquad (t\in\mathbb{Z}) y=y0+(a,b)at(tZ)

x x x 同理,让 t t t 取所有整数,可以得到方程(1)的全部整数解:
{ x = x 0 − b ( a , b ) t y = y 0 + a ( a , b ) t ( t ∈ Z ) \begin{cases} x=x_0-\frac{b}{(a,b)}t \\ y=y_0+\frac{a}{(a,b)}t \qquad (t\in\mathbb{Z})\end{cases} {x=x0(a,b)bty=y0+(a,b)at(tZ)


扩展欧几里得算法

算法的简介

扩展欧几里得算法是在普通欧几里得算法(用于求最大公约数)的基础上推广出来的,它不仅可以计算出两个整数的最大公约数,还可以给出一组整数解(即贝祖数)来满足裴蜀恒等式。


算法的应用场景

  1. 求乘法逆元
    对于两个整数 a a a m m m,想求 a a a 在模 m m m 意义下的乘法逆元,可以使用扩展欧几里得算法求出 g c d ( a , m ) = 1 gcd(a,m)=1 gcd(a,m)=1 的整数解 x x x,则 x m o d    m x \mod m xmodm 就是 a a a 的乘法逆元。

  2. 解不定线性方程组
    对于形如 a x + b y = c ax + by = c ax+by=c 的一元二次不定线性方程组,可以直接用扩展欧几里得算法求出一组整数解。

  3. 求线性同余方程的解
    把线性同余方程 a x ≡ b ( m o d    m ) ax ≡ b (\mod m) axb(modm) 转化为 a x + m y = b ax + my = b ax+my=b 的形式,就可以用扩展欧几里得算法求出其整数解。

  4. RSA加密算法
    在 RSA 加密算法中,需要求出乘法逆元,这可以有效通过扩展欧几里得算法实现。

  5. 椭圆曲线加密算法
    椭圆曲线加密算法中也需要用到扩展欧几里得算法求逆。

  6. 编码理论中的 syndrome 译码
    在编码理论中,需要解线性方程来进行 syndrome 译码,扩展欧几里得是重要工具。


算法实现过程的证明

(1) 当 b = 0 b=0 b=0

a x + b y = g c d ( a , 0 ) ax+by=gcd(a,0) ax+by=gcd(a,0),也就是: a x = g c d ( a , 0 ) = a ax=gcd(a,0)=a ax=gcd(a,0)=a,所以 x = 1 x=1 x=1

那么 y y y 呢?因为普遍意义上的求贝祖数,一般是指不小于零的一组解,那么不小于 0 0 0 的第一个可能 y y y 值就是: y = 0 y=0 y=0,所以,可以将 { x = 1 y = 0 \left\{\begin{array}{l}x = 1 \\ y = 0\end{array}\right. {x=1y=0 做为一组特解返回,也就是返回了一组不小于零的贝祖数。

(2) 当 b ≠ 0 b≠0 b=0

a x + b y = g c d ( a , b ) ax+by=gcd(a,b) ax+by=gcd(a,b) 【原式】

g c d ( b , a gcd(b,a gcd(b,a % b ) = g c d ( a , b ) b)=gcd(a,b) b)=gcd(a,b) 【辗转相除法】

a x + b y = g c d ( a , b ) = g c d ( b , a ax+by=gcd(a,b)=gcd(b,a%b)=b⋅x_0+(a ax+by=gcd(a,b)=gcd(b,a % b ) ⋅ y 0 b)⋅y_0 b)y0 ① 【变量互换,最终是一致的】

a a a % b = a − ⌊ a b ⌋ ⋅ b b=a−⌊\frac {a}{b}⌋⋅b b=abab ②【用整除向下取整来描述扣去 a a a 中所有的 b b b,剩下的就是余数】

将②代入①
a x + b y = b x 0 + ( a − ⌊ a b ⌋ ⋅ b ) ⋅ y 0 ax+by=bx_0+(a−⌊\frac a b⌋⋅b)⋅y_0 ax+by=bx0+(abab)y0

a x + b y = a y 0 + b ( x 0 − ⌊ a b ⌋ ⋅ y 0 ) ax+by=ay_0+b(x_0−⌊\frac a b⌋⋅y_0) ax+by=ay0+b(x0bay0)

{ x = y 0 y = x 0 − ⌊ a b ⌋ ⋅ y 0 \left\{\begin{array}{l}x = y_0 \\ y = x_0−⌊\frac a b⌋⋅y_0\end{array}\right. {x=y0y=x0bay0


解不定线性方程组

题目描述:
给定 n n n 对正整数 a i , b i a_i,b_i ai,bi,对于每对数,求出一组 x i , y i x_i,y_i xi,yi,使其满足 a i × x i + b i × y i = g c d ( a i , b i ) a_i×x_i+b_i×y_i=gcd(a_i,b_i) ai×xi+bi×yi=gcd(ai,bi)

输入格式:

第一行包含整数 n n n

接下来 n n n 行,每行包含两个整数 a i , b i a_i,b_i ai,bi

输出格式:
输出共 n n n 行,对于每组 a i , b i a_i,b_i ai,bi,求出一组满足条件的 x i , y i x_i,y_i xi,yi,每组结果占一行。

本题答案不唯一,输出任意满足条件的 x i , y i x_i,y_i xi,yi 均可。

数据范围:
1 ≤ n ≤ 1 0 5 1≤n≤10^5 1n105

1 ≤ a i , b i ≤ 2 × 1 0 9 1≤a_i,b_i≤2×10^9 1ai,bi2×109

输入样例:

2
4 6
8 18

输出样例:

-1 1
-2 1

代码实现

#define _CRT_SECURE_NO_WARNINGS
#include
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); // 对x,y进行颠倒方便后面赋值
	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;
	}
	return 0;
}

线性同余方程

题目描述:
给定 n n n 组数据 a i , b i , m i a_i,b_i,m_i ai,bi,mi,对于每组数求出一个 x i x_i xi,使其满足 a i × x i ≡ b i ( m o d    m i ) a_i×x_i≡b_i(\mod m_i) ai×xibi(modmi),如果无解则输出 impossible

输入格式:
第一行包含整数 n n n

接下来 n n n 行,每行包含一组数据 a i , b i , m i a_i,b_i,m_i ai,bi,mi

输出格式:
输出共 n n n 行,每组数据输出一个整数表示一个满足条件的 x i x_i xi,如果无解则输出 impossible

每组数据结果占一行,结果可能不唯一,输出任意一个满足条件的结果均可。

输出答案必须在 int 范围之内。

数据范围:
1 ≤ n ≤ 1 0 5 , 1 ≤ a i , b i , m i ≤ 2 × 1 0 9 1≤n≤10^5,1≤a_i,b_i,m_i≤2×10^9 1n105,1ai,bi,mi2×109

输入样例:

2
2 3 6
4 3 5

输出样例:

impossible
-3

代码实现

#define _CRT_SECURE_NO_WARNINGS
#include
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 n;
	cin >> n;
	while (n--)
	{
		int a, b, m, x, y;
		cin >> a >> b >> m;
		int d = exgcd(a, m, x, y);
		if (b % d) cout << "impossible" << endl;
		else cout << x * b / d % m << endl; // 取模m是为了可以保证在int范围内
	}
	return 0;
}

中国剩余定理

中国剩余定理的定义

中国剩余定理是数论中的一个重要定理,它解决了这样一个问题:对于多个模数 m 1 , m 2 , . . . m n m_1,m_2,...m_n m1,m2,...mn,求同时满足余数等式:

x ≡ a 1 ( m o d    m 1 ) x ≡ a_1 (\mod m1) xa1(modm1)
x ≡ a 2 ( m o d    m 2 ) x ≡ a_2 (\mod m2) xa2(modm2)
. . . ... ...
x ≡ a n ( m o d    m n ) x ≡ a_n (\mod mn) xan(modmn)的整数解。

该定理指出,如果 m 1 , m 2 , . . . m n m_1,m_2,...m_n m1,m2,...mn 两两互质,则上述剩余系数方程组存在整数解,且解在模 M = m 1 ∗ m 2 ∗ . . . ∗ m n M=m_1*m_2*...*m_n M=m1m2...mn 意义下是唯一的。其中 M M M 称为方程组的模。

定理给出了求解此类方程组的方法:

M i = M / m i M_i=M/{m_i} Mi=M/mi M i ′ M_i' Mi M i M_i Mi m i m_i mi 的逆元,则方程组的整数解为:

x = a 1 M 1 M 1 ′ + a 2 M 2 M 2 ′ + . . . + a n M n M n ′ ( m o d    M ) x = a_1M_1M_1' + a_2M_2M_2' + ... + a_nM_nM_n' (\mod M) x=a1M1M1+a2M2M2+...+anMnMn(modM)


表达整数的奇怪方式(高难)

题目描述:
给定 2 n 2n 2n 个整数 a 1 , a 2 , … , a n a_1,a_2,…,a_n a1,a2,,an m 1 , m 2 , … , m n m_1,m_2,…,m_n m1,m2,,mn,求一个最小的非负整数 x x x,满足 ∀ i ∈ [ 1 , n ] , x ≡ m i ( m o d    a i ) ∀i∈[1,n],x≡m_i(\mod a_i) i[1,n],xmi(modai)

输入格式:
1 1 1 行包含整数 n n n

2 2 2 ~ ( n + 1 ) (n+1) (n+1) 行:每行包含两个整数 a i a_i ai m i m_i mi,数之间用空格隔开。

输出格式:
输出最小非负整数 x x x,如果 x x x 不存在,则输出 −1

如果存在 x x x,则数据保证 x x x 一定在 64 64 64 位整数范围内。

数据范围:
1 ≤ a i ≤ 2 31 − 1 , 0 ≤ m i < a i 1≤a_i≤2^{31}−1,0≤m_i1ai2311,0mi<ai

1 ≤ n ≤ 25 1≤n≤25 1n25

输入样例:

2
8 7
11 9

输出样例:

31

实现思路

本题中并未说 m 1 , m 2 , … , m n m_1,m_2,…,m_n m1,m2,,mn 之间两两互质,不能直接使用中国剩余定理,需要重新推导。

{ x ≡ m 1 ( m o d    a 1 ) ① x ≡ m 2 ( m o d    a 2 ) ② . . . x ≡ m n ( m o d    a n ) \left\{\begin{array}{l} x≡m_1(\mod a_1)\qquad①\\x≡m_2(\mod a_2)\qquad②\\...\\x≡m_n(\mod a_n)\end{array}\right. xm1(moda1)xm2(moda2)...xmn(modan)
先计算两个线性同余方程组的解,之后依此类推
① ① 可以写成 x = k 1 ∗ a 1 + m 1 ③ x=k_1∗a_1+m_1 \qquad③ x=k1a1+m1
② ② 可以写成 x = k 2 ∗ a 2 + m 2 ④ x=k_2∗a_2+m_2 \qquad④ x=k2a2+m2

③ = ④ ③ = ④ =
⇒ k 1 ∗ a 1 + m 1 = k 2 ∗ a 2 + m 2 ⇒k_1∗a_1+m_1=k_2∗a_2+m_2 k1a1+m1=k2a2+m2
⇒ k 1 ∗ a 1 − k 2 ∗ a 2 = m 2 − m 1 ⑤ ⇒k_1∗a_1−k_2∗a_2=m_2−m_1 \qquad⑤ k1a1k2a2=m2m1

如前文提到的裴蜀定理求解二元不定方程,易得

{ k 1 = k 1 + a 2 ( a 1 , a 2 ) k k 2 = k 2 + a 1 ( a 1 , a 2 ) k ( k ∈ Z ) \begin{cases} k_1=k_1+\frac{a_2}{(a_1,a_2)}k \\ k_2=k_2+\frac{a_1}{(a_1,a_2)}k \qquad (k\in\mathbb{Z})\end{cases} {k1=k1+(a1,a2)a2kk2=k2+(a1,a2)a1k(kZ)

d d d 来表示 ( a 1 , a 2 ) (a_1,a_2) (a1,a2) k 1 k_1 k1 的通解代入 ③ ③

⇒ x = k 1 ∗ a 1 + m 1 ⇒x=k_1∗a_1+m_1 x=k1a1+m1
⇒ x = ( k 1 + k ∗ a 2 d ) ∗ a 1 + m 1 ⇒x=(k_1+k∗\frac {a_2}{d})∗a_1+m_1 x=(k1+kda2)a1+m1
⇒ x = k ∗ a 2 d ∗ a 1 + k 1 ∗ a 1 + m 1 ⇒x=k∗\frac {a_2}{d}∗a_1+k_1∗a_1+m_1 x=kda2a1+k1a1+m1

a 1 = a 2 d ∗ a 1 a_1=\frac {a_2}{d}∗a_1 a1=da2a1 m 1 = k 1 ∗ a 1 + m 1 m_1=k_1∗a_1+m_1 m1=k1a1+m1,就和原来的方程一个样子,但化简掉了一个方程。

最终一共合并 n − 1 n-1 n1 次,每次都是将上一步得到的新式子与下一个式子合并,直到最后只剩一个式子。这个式子就是包含了全部信息的合并后的式子,也就了满足我们的答案。


代码实现

注意正负性

#define _CRT_SECURE_NO_WARNINGS
#include
using namespace std;

long long exgcd(long long a, long long b, long long& x, long long& y)
{
	if (!b)
	{
		x = 1, y = 0;
		return a;
	}
	long long d = exgcd(b, a % b, y, x);
	y -= a / b * x;
	return d;
}
int main()
{
	int n;
	cin >> n;
	long long x = 0, m1, a1;
	cin >> a1 >> m1;
	while (--n)
	{
		long long a2, m2;
		cin >> a2 >> m2;
		long long k1, k2;
		long long d = exgcd(a1, -a2, k1, k2);

		if ((m2 - m1) % d) // 如果不是0,则无解
		{
			x = -1;
			break;
		}

		// 求 a1*k1+(-a2)*k2=m2-m1 的一组解,需要翻 (m2-m1)/d倍
		k1 *= (m2 - m1) / d;

		// 求最小正整数特解
		long long t = abs(a2 / d);
		k1 = (k1 % t + t) % t;

		// 用通解更新a1和m1,准备下一轮合并
		m1 = k1 * a1 + m1;
		a1 = abs(a1 / d * a2);
	}

	if (x != -1) x = (m1 % a1 + a1) % a1; // 保证最小正整数
	cout << x << endl;
	return 0;
}

你可能感兴趣的:(从零开始的算法打灰,算法,c++)