扩展中国剩余定理
中国剩余定理
中国剩余定理
考虑一组模线性同余方程:
{ x ≡ a 1 ( m o d m 1 ) x ≡ a 2 ( m o d m 2 ) . . . x ≡ a k ( m o d m k ) \begin{cases} x\equiv a_1\pmod{m1} \\ x\equiv a_2\pmod{m2}\\ .\\ .\\ .\\ x\equiv a_k\pmod{mk}\\ \end{cases} ⎩ ⎨ ⎧x≡a1(modm1)x≡a2(modm2)...x≡ak(modmk)
其中, a 1 , a 2 , . . . , a k a_1,a_2,...,a_k a1,a2,...,ak是给定整数,而 m 1 , m 2 , . . . , m k m_1,m_2,...,m_k m1,m2,...,mk是两两互质的正整数。
具体步骤如下:
计算模数的乘积:$ M = m_1 ⋅m_2⋅…⋅m_k$
计算辅助模数: 对于每个 i,计算 M i = M / m i M_i=M/m_i Mi=M/mi
计算模反元素: 对于每个 i,找到 M i − 1 M_i^{-1} Mi−1,使得 M i ⋅ M i − 1 ≡ 1 ( m o d m i ) M_i\cdot M_i^{-1}\equiv 1 \pmod{m_i} Mi⋅Mi−1≡1(modmi)。
计算解: 最终解 x 由以下公式给出:$\left ( a_1\cdot M_1 \cdot M_1^{-1}+a_2\cdot M_2 \cdot M_2^{-1}+…+a_k\cdot M_k \cdot M_k^{-1}\right ) \bmod M $
中国剩余定理在密码学、编码理论、计算机科学等领域有着广泛的应用。它不仅用于解决同余方程组,还被应用于很多领域,例如数据传输、数字签名等。
扩展中国剩余定理
考虑一组模线性同余方程:
{ x ≡ a 1 ( m o d m 1 ) x ≡ a 2 ( m o d m 2 ) . . . x ≡ a k ( m o d m k ) \begin{cases} x\equiv a_1\pmod{m1} \\ x\equiv a_2\pmod{m2}\\ .\\ .\\ .\\ x\equiv a_k\pmod{mk}\\ \end{cases} ⎩ ⎨ ⎧x≡a1(modm1)x≡a2(modm2)...x≡ak(modmk)
其中, a 1 , a 2 , . . . , a k a_1,a_2,...,a_k a1,a2,...,ak是给定整数,而 m 1 , m 2 , . . . , m k m_1,m_2,...,m_k m1,m2,...,mk是正整数。
x = k 1 m 1 + a 1 x=k_1m_1+a_1 x=k1m1+a1, x = k 2 m 2 + a 2 x=k_2m_2+a_2 x=k2m2+a2。
所以 k m 1 + a 1 = k m 2 + a 2 km_1+a_1=km_2+a_2 km1+a1=km2+a2
从而 k 1 m 1 − k 2 m 2 = a 2 − a 1 k_1m_1-k_2m_2=a_2-a_1 k1m1−k2m2=a2−a1,其中 k 1 , k 2 k_1,k_2 k1,k2未知。
k 1 m i n = ( ( k 0 ⋅ a 2 − a 1 g c d ( m 1 , m 2 ) ) % m 2 g c d ( m 1 , m 2 ) + m 2 g c d ( m 1 , m 2 ) ) % m 2 g c d ( m 1 , m 2 ) k_1min=((k_0\cdot \frac{a_2-a_1}{gcd(m_1,m_2)})\%\frac{m_2}{gcd(m_1,m_2)} +\frac{m_2}{gcd(m_1,m_2)})\%\frac{m_2}{gcd(m_1,m_2)} k1min=((k0⋅gcd(m1,m2)a2−a1)%gcd(m1,m2)m2+gcd(m1,m2)m2)%gcd(m1,m2)m2。
其中 k 0 k_0 k0为 k 1 m 1 − k 2 m 2 = g c d ( m 1 , m 2 ) k_1m_1-k_2m_2=gcd(m_1,m_2) k1m1−k2m2=gcd(m1,m2)的特解, k 1 m i n k_1min k1min为最小正整数解。
x = ( k 1 m i n + k ⋅ m 2 g c d ( m 1 , m 2 ) ) m 1 + a 1 = m 1 m 2 g c d ( m 1 , m 2 ) k + k 1 m i n + a 1 x=(k_1min+k\cdot\frac{m_2}{gcd(m_1,m_2)} )m_1+a_1=\frac{m_1m_2}{gcd(m_1,m_2)}k+k_1min+a_1 x=(k1min+k⋅gcd(m1,m2)m2)m1+a1=gcd(m1,m2)m1m2k+k1min+a1
m 1 = m 1 m 2 g c d ( m 1 , m 2 m_1=\frac{m_1m_2}{gcd(m_1,m_2} m1=gcd(m1,m2m1m2, a 1 = k 1 m i n + a 1 a_1=k_1min+a_1 a1=k1min+a1。
代码实现
#include <cstdio>
#include <iostream>
#include <cmath>using namespace std;typedef __int128 ll;int n;ll read() {ll x = 0, f = 1;char c = getchar();while (c < '0' || c>'9') {if (c == '-') f = -1;c = getchar(); }while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();return x * f;
}
void write(ll x) {if(x == 0){putchar('0');return;}if(x < 0) putchar('-'), x = -x;if(x > 9) write(x / 10);putchar(x % 10 + '0');
}ll my_abs(ll x){return x >= 0 ? x : -x;
}
ll exgcd(ll a, ll b, ll &x, ll &y){if(b == 0){x = 1, y = 0;return a;}ll d = exgcd(b, a % b, x, y);ll t = x;x = y;y = t- a / b * y;return d;
}int main(){cin >> n;ll m1, a1, m2, a2, k1, k2;m1 = read(), a1 = read();bool flag = false;for (int i = 1; i <= n-1; i++){m2 = read(), a2 = read();ll d = exgcd(m1, m2, k1, k2);if ((a2 - a1) % d){flag = true;continue;} ll r = my_abs(m2 / d);k1 = (((k1 * (a2 - a1) / d ) % r) + r) % r;a1 = m1 * k1 + a1, m1 = m2 / d * m1;}if (flag) puts("-1");else write(a1);return 0;
}
例题
- 表达整数的奇怪方式
#include <cstdio>
#include <iostream>
#include <cmath>using namespace std;typedef long long ll;int n;ll exgcd(ll a, ll b, ll &x, ll &y){if(b == 0){x = 1, y = 0;return a;}ll d = exgcd(b, a%b, x, y);ll t = x;x = y;y = t- a / b * y;return d;
}/*
x=ka+m
x=k1*a1+m1=k2*a2+m2
k1*a1-k2*a2=m2-m1
k1*a1+k2*a2=m2-m1
k0=(k1*(m2-m1)/d%(a2/d)+a2/d)%(a2/d) 其中:k0为最小正整数解x=(k0+k*a2/d)*a1+m1=a1*a2/d*k+k0*a1+m1
a1=a1*a2/d,m1=k0*a1+m1;
*/int main(){ll a1, a2, m1, m2, k1, k2;scanf("%d", &n);scanf("%lld%lld", &a1, &m1);bool f = 0;for(int i=1; i <= n-1; i++){scanf("%lld%lld", &a2, &m2);ll d = exgcd(a1, a2, k1, k2);if((m2 - m1) % d){f = 1;continue;}ll k0 = (k1 * (m2 - m1) / d % (a2 / d) + a2 / d) % (a2 / d);m1 = k0 * a1 + m1, a1 = a1 * a2 / d;// m1=k0*a1+m1 和 a1=a1*a2/d 顺序不能换 }if(f) puts("-1");else printf("%lld\n", m1);return 0;
}