Try   HackMD

中國剩餘定理 不互質

本頁面已停止更新 新的版本可至此處觀看

題目

  • 給你一套方程組如下,其中模數(
    ki
    )不一定互質,求出最小正整數解
    x
    ,如果沒有則輸出
    1

xp1(modk1)xp2(modk2)......xpn(modkn)

想法

先以兩兩來看

{xp1(modk1)xp2(modk2)

{x=k1x1+p1x=k2x2+p2

k1x1+p1=k2x2+p2

k1x1+(k2)x2=p2p1

我們下面主要要解的是

x1 所以跟
x2
係數的正負沒什麼關西,所以以下都寫正號

貝祖定理: 在

ax+by=m 中, 若且唯若
m
a
b
的最大公因數
gcd(a,b)
的倍數,有整數解

x1,x2 有解,則整除
gcd(k1,k2)p2p1
, 如果不是的話代表無解

gcd(k1,k2)=d,與
p2p1=c

k1d×x1+k2d×x2=cd

其中

k1d
k2d
互質

模逆元移向證明:

a×bc(modm)
兩邊同乘
b1a×(b×b1)c×b1(modm)

ac×b1(modm)

x1
k1d×x1+k2d×x2=1
的解則,
x1(k1d)1(modk2d)
,這個可以用 extgcd 算出來
(2)
式中
x1
的解可為
ansx1cd×(k1d)1(modk2d)
(
ansx1
(2)
式中
x1
的解)代回
(1)

X=k1×ansx1+p1
X=k1[cd×(k1d)1+tmp×k2d]+p1

X=k1×cd×(k1d)1+tmp×k1k2d+p1

而新的同餘式就是
xk1×cd×(k1d)1+p1(modk1k2d)

即可表達為
xk1×ansx1+p1(modk1k2d)

k1k2=gcd(k1,k2)×lcm(k1,k2)
k1k2d=lcm

故若得前
i1
個式子合併得到
xX(modlcm)

而與第
i
式合併為
x=ansxi×lcm+X(modlcm×kid)

以上面來的

x=k1x1+p1 說,目前的
k1=lcm,p1=X,x1=ansx1=ansxi

輾轉相除法:

gcd(a,b)=gcd(b,b%a)
d=gcd(a,b),a=nd,b=md

一直互相相減之後就會得到
d

https://hackmd.io/@Koios/rJ_lER719

證明: 為什麼除以一個數取模等於乘以這個數的模逆元
證明圖片

擴展歐里基德推導:

ax+by=gcd(a,b)
bx1+(a%b)y1=gcd(a,b)

ax+by=bx1+(ab×ab)y1

整理右式
ax+by=ay1+b(x1aby1)

推得轉移式
x=y1,y=x1aby1

pii ex_gcd(int a,int b) { if (b == 0) { return {1, 0}; } pii p = ex_gcd(b, a%b); int x = p.second; int y = p.first - p.second*(a/b); return {x,y}; }

實作

#include <bits/stdc++.h> #define int long long #define pii pair<int,int> using namespace std; int p[1001], k[1001]; int n; pii ex_gcd(int a,int b) { if (b == 0) { return {1, 0}; } pii p = ex_gcd(b, a%b); int x = p.second; int y = p.first - p.second*(a/b); return {x,y}; } int china () { int X = p[1]; int lcm = k[1]; /* x ≡ p1(mod k1) x ≡ p2(mod k2) x = x1 * k1 + p1 x = x2 * k2 + p2 x1 * k1 - x2 * k2 = p2 - p1 */ // 可以假設 k1 是已經有的 // k2 是現在要算的 for (int i = 2; i <= n; i++) { int c = p[i] - X; // p2-p1 int d = __gcd(k[i], lcm); //k1*k2 if (c % d != 0) return -1; auto [x,y] = ex_gcd(lcm/d, k[i]/d); // (lcm/d)*(x1) + (k2/d)*(x2) = 1 x = (c) / d * x % (k[i] / d); // 因為是模逆元, 被 模過 k[i]*d 下, 要是又超過了就要在模一次 X += lcm*x; // X 每次都要更新 lcm = lcm / d * k[i]; // lcm 每次都要更新 // x ≡ X (mod lcm) X %= lcm; } return (X > 0) ? X : X + lcm; } signed main() { while(cin >> n) { for (int i = 1; i <= n; i++) { cin >> k[i] >> p[i]; } cout << china() << '\n'; } }

credit :

互質

{xp1(modk1)xp2(modk2)xpk(modkn)

  • M=k1×k2××kn
  • Mi=Mki
  • tiMi1(modki)
  • X=p1t1M1+p2t2M2++pntnMn
  • x=X(modM)
int china () { int M = 1; for (int i = 1; i <= n; i++) M *= k[i]; int ret = 0; for (int i = 1; i <= n; i++) { int Mi = M / k[i]; auto [t, y] = ex_gcd(Mi, k[i]); ret = (ret + p[i]*Mi*t) % M; // 最後要去在 1 ~ M 的解, 所以要 mod M } return (ret + M) % M; }
tags: algo