若 a , b a,b a,b 是正整数,且 g c d ( a , b ) = d gcd(a,b)=d gcd(a,b)=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(t∈Z)
其中 ( 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(x−x0)+b(y−y0)=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(x−x0)+(a,b)b(y−y0)=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(y−y0)=(a,b)a(x−x0)
因为:
( 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∣(y−y0)
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(t∈Z)
对 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(t∈Z)
扩展欧几里得算法是在普通欧几里得算法(用于求最大公约数)的基础上推广出来的,它不仅可以计算出两个整数的最大公约数,还可以给出一组整数解(即贝祖数)来满足裴蜀恒等式。
求乘法逆元
对于两个整数 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 的乘法逆元。
解不定线性方程组
对于形如 a x + b y = c ax + by = c ax+by=c 的一元二次不定线性方程组,可以直接用扩展欧几里得算法求出一组整数解。
求线性同余方程的解
把线性同余方程 a x ≡ b ( m o d m ) ax ≡ b (\mod m) ax≡b(modm) 转化为 a x + m y = b ax + my = b ax+my=b 的形式,就可以用扩展欧几里得算法求出其整数解。
RSA加密算法
在 RSA 加密算法中,需要求出乘法逆元,这可以有效通过扩展欧几里得算法实现。
椭圆曲线加密算法
椭圆曲线加密算法中也需要用到扩展欧几里得算法求逆。
编码理论中的 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=a−⌊ba⌋⋅b ②【用整除向下取整来描述扣去 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+(a−⌊ba⌋⋅b)⋅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(x0−⌊ba⌋⋅y0)
∴ { 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=x0−⌊ba⌋⋅y0
题目描述:
给定 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 1≤n≤105
1 ≤ a i , b i ≤ 2 × 1 0 9 1≤a_i,b_i≤2×10^9 1≤ai,bi≤2×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×xi≡bi(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 1≤n≤105,1≤ai,bi,mi≤2×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) x≡a1(modm1)
x ≡ a 2 ( m o d m 2 ) x ≡ a_2 (\mod m2) x≡a2(modm2)
. . . ... ...
x ≡ a n ( m o d m n ) x ≡ a_n (\mod mn) x≡an(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=m1∗m2∗...∗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],x≡mi(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_i
1 ≤ n ≤ 25 1≤n≤25 1≤n≤25
输入样例:
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. ⎩ ⎨ ⎧x≡m1(moda1)①x≡m2(moda2)②...x≡mn(modan)
先计算两个线性同余方程组的解,之后依此类推
① ① ① 可以写成 x = k 1 ∗ a 1 + m 1 ③ x=k_1∗a_1+m_1 \qquad③ x=k1∗a1+m1③
② ② ② 可以写成 x = k 2 ∗ a 2 + m 2 ④ x=k_2∗a_2+m_2 \qquad④ x=k2∗a2+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 ⇒k1∗a1+m1=k2∗a2+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⑤ ⇒k1∗a1−k2∗a2=m2−m1⑤
如前文提到的裴蜀定理求解二元不定方程,易得
{ 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(k∈Z)
用 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=k1∗a1+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+k∗da2)∗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=k∗da2∗a1+k1∗a1+m1
令 a 1 = a 2 d ∗ a 1 a_1=\frac {a_2}{d}∗a_1 a1=da2∗a1 与 m 1 = k 1 ∗ a 1 + m 1 m_1=k_1∗a_1+m_1 m1=k1∗a1+m1,就和原来的方程一个样子,但化简掉了一个方程。
最终一共合并 n − 1 n-1 n−1 次,每次都是将上一步得到的新式子与下一个式子合并,直到最后只剩一个式子。这个式子就是包含了全部信息的合并后的式子,也就了满足我们的答案。
注意正负性
#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;
}