Перейти до основного вмісту

Китайська теорема про остачі

Китайську теорему про остачі (далі в цій статті ми позначатимемо її як CRT) відкрив китайський математик Сунь Цзи.

Коли підходить цей алгоритм?
  • Чи задача дає систему конгруенцій aai(modmi)a \equiv a_i \pmod{m_i} і просить знайти aa за модулем добутку mim_i?
  • Чи модулі mim_i попарно взаємно прості (інакше потрібен окремий розбір степенів простих, описаний у статті)? (якщо модуль один і простий → Лінійне конгруентне рівняння)
  • Чи ви хочете уникнути довгої арифметики, виконуючи обчислення над великим числом через його остачі за кількома малими модулями? (якщо так, для відновлення числа → Алгоритм Гарнера)

Формулювання

Нехай m=m1m2mkm = m_1 \cdot m_2 \cdots m_k, де mim_i попарно взаємно прості. Окрім mim_i, нам також задано систему конгруенцій

{aa1(modm1)aa2(modm2)aak(modmk)\left\{\begin{array}{rcl} a & \equiv & a_1 \pmod{m_1} \\ a & \equiv & a_2 \pmod{m_2} \\ & \vdots & \\ a & \equiv & a_k \pmod{m_k} \end{array}\right.

де aia_i — деякі задані константи. Початкове формулювання CRT стверджує, що задана система конгруенцій завжди має рівно один розв'язок за модулем mm.

Наприклад, система конгруенцій

{a2(mod3)a3(mod5)a2(mod7)\left\{\begin{array}{rcl} a & \equiv & 2 \pmod{3} \\ a & \equiv & 3 \pmod{5} \\ a & \equiv & 2 \pmod{7} \end{array}\right.

має розв'язок 2323 за модулем 105105, бо 23mod3=223 \bmod{3} = 2, 23mod5=323 \bmod{5} = 3 і 23mod7=223 \bmod{7} = 2. Кожен розв'язок ми можемо записати як 23+105k23 + 105\cdot k для kZk \in \mathbb{Z}.

Наслідок

Наслідком CRT є те, що рівняння

xa(modm)x \equiv a \pmod{m}

еквівалентне системі рівнянь

{xa1(modm1)xak(modmk)\left\{\begin{array}{rcl} x & \equiv & a_1 \pmod{m_1} \\ & \vdots & \\ x & \equiv & a_k \pmod{m_k} \end{array}\right.

(як і вище, вважаємо, що m=m1m2mkm = m_1 m_2 \cdots m_k, а mim_i попарно взаємно прості).

Розв'язок для двох модулів

Розглянемо систему з двох рівнянь для взаємно простих m1,m2m_1, m_2:

{aa1(modm1)aa2(modm2)\left\{\begin{align} a &\equiv a_1 \pmod{m_1} \\ a &\equiv a_2 \pmod{m_2} \\ \end{align}\right.

Ми хочемо знайти розв'язок для a(modm1m2)a \pmod{m_1 m_2}. За допомогою розширеного алгоритму Евкліда ми можемо знайти коефіцієнти Безу n1,n2n_1, n_2 такі, що

n1m1+n2m2=1.n_1 m_1 + n_2 m_2 = 1.

Насправді n1n_1 і n2n_2 — це просто обернені елементи за модулем для m1m_1 і m2m_2 за модулями m2m_2 і m1m_1. Маємо n1m11(modm2)n_1 m_1 \equiv 1 \pmod{m_2}, отже n1m11(modm2)n_1 \equiv m_1^{-1} \pmod{m_2}, і навпаки n2m21(modm1)n_2 \equiv m_2^{-1} \pmod{m_1}.

За допомогою цих двох коефіцієнтів ми можемо задати розв'язок:

a=a1n2m2+a2n1m1modm1m2a = a_1 n_2 m_2 + a_2 n_1 m_1 \bmod{m_1 m_2}

Легко переконатися, що це справді розв'язок, обчисливши amodm1a \bmod{m_1} і amodm2a \bmod{m_2}.

aa1n2m2+a2n1m1(modm1)a1(1n1m1)+a2n1m1(modm1)a1a1n1m1+a2n1m1(modm1)a1(modm1)\begin{array}{rcll} a & \equiv & a_1 n_2 m_2 + a_2 n_1 m_1 & \pmod{m_1}\\ & \equiv & a_1 (1 - n_1 m_1) + a_2 n_1 m_1 & \pmod{m_1}\\ & \equiv & a_1 - a_1 n_1 m_1 + a_2 n_1 m_1 & \pmod{m_1}\\ & \equiv & a_1 & \pmod{m_1} \end{array}

Зауважимо, що китайська теорема про остачі також гарантує, що за модулем m1m2m_1 m_2 існує лише 11 розв'язок. Це теж легко довести.

Припустимо, що ми маємо два різні розв'язки xx та yy. Оскільки xai(modmi)x \equiv a_i \pmod{m_i} і yai(modmi)y \equiv a_i \pmod{m_i}, то звідси випливає, що xy0(modmi)x − y \equiv 0 \pmod{m_i}, а отже xy0(modm1m2)x − y \equiv 0 \pmod{m_1 m_2} або, що еквівалентно, xy(modm1m2)x \equiv y \pmod{m_1 m_2}. Тож xx та yy насправді є одним і тим самим розв'язком.

Розв'язок для загального випадку

Індуктивний розв'язок

Оскільки m1m2m_1 m_2 взаємно просте з m3m_3, ми можемо індуктивно багаторазово застосовувати розв'язок для двох модулів для будь-якої кількості модулів. Спочатку, використовуючи перші дві конгруенції, ми обчислюємо b2:=a(modm1m2)b_2 := a \pmod{m_1 m_2}, потім можемо обчислити b3:=a(modm1m2m3)b_3 := a \pmod{m_1 m_2 m_3} за допомогою конгруенцій ab2(modm1m2)a \equiv b_2 \pmod{m_1 m_2} та aa3(modm3)a \equiv a_3 \pmod {m_3}, і так далі.

Пряма побудова

Можлива пряма побудова, схожа на інтерполяцію Лагранжа.

Нехай Mi:=ijmjM_i := \prod_{i \neq j} m_j — добуток усіх модулів, крім mim_i, а NiN_i — обернені елементи за модулем Ni:=Mi1modmiN_i := M_i^{-1} \bmod{m_i}. Тоді розв'язком системи конгруенцій є:

ai=1kaiMiNi(modm1m2mk)a \equiv \sum_{i=1}^k a_i M_i N_i \pmod{m_1 m_2 \cdots m_k}

Ми можемо перевірити, що це справді розв'язок, обчисливши amodmia \bmod{m_i} для всіх ii. Оскільки MjM_j є кратним mim_i для iji \neq j, маємо

aj=1kajMjNj(modmi)aiMiNi(modmi)aiMiMi1(modmi)ai(modmi)\begin{array}{rcll} a & \equiv & \sum_{j=1}^k a_j M_j N_j & \pmod{m_i} \\ & \equiv & a_i M_i N_i & \pmod{m_i} \\ & \equiv & a_i M_i M_i^{-1} & \pmod{m_i} \\ & \equiv & a_i & \pmod{m_i} \end{array}

Реалізація

struct Congruence {
long long a, m;
};

long long chinese_remainder_theorem(vector<Congruence> const& congruences) {
long long M = 1;
for (auto const& congruence : congruences) {
M *= congruence.m;
}

long long solution = 0;
for (auto const& congruence : congruences) {
long long a_i = congruence.a;
long long M_i = M / congruence.m;
long long N_i = mod_inv(M_i, congruence.m);
solution = (solution + a_i * M_i % M * N_i) % M;
}
return solution;
}

Розв'язок для не взаємно простих модулів

Як уже згадувалося, наведений вище алгоритм працює лише для взаємно простих модулів m1,m2,mkm_1, m_2, \dots m_k.

У випадку не взаємно простих модулів система конгруенцій має рівно один розв'язок за модулем lcm(m1,m2,,mk)\text{lcm}(m_1, m_2, \dots, m_k) або не має розв'язку взагалі.

Наприклад, у наведеній нижче системі перша конгруенція означає, що розв'язок непарний, а друга конгруенція означає, що розв'язок парний. Неможливо, щоб число було одночасно непарним і парним, тому розв'язку, очевидно, немає.

{a1(mod4)a2(mod6)\left\{\begin{align} a & \equiv 1 \pmod{4} \\ a & \equiv 2 \pmod{6} \end{align}\right.

Визначити, чи має система розв'язок, доволі просто. А якщо він є, ми можемо застосувати початковий алгоритм для розв'язання трохи зміненої системи конгруенцій.

Одна конгруенція aai(modmi)a \equiv a_i \pmod{m_i} еквівалентна системі конгруенцій aai(modpjnj)a \equiv a_i \pmod{p_j^{n_j}}, де p1n1p2n2pknkp_1^{n_1} p_2^{n_2}\cdots p_k^{n_k} — це розклад на прості множники числа mim_i.

Скориставшись цим фактом, ми можемо перетворити систему конгруенцій на систему, в якій модулями є лише степені простих чисел. Наприклад, наведена вище система конгруенцій еквівалентна:

{a1(mod4)a20(mod2)a2(mod3)\left\{\begin{array}{ll} a \equiv 1 & \pmod{4} \\ a \equiv 2 \equiv 0 & \pmod{2} \\ a \equiv 2 & \pmod{3} \end{array}\right.

Оскільки спочатку деякі модулі мали спільні множники, ми отримаємо кілька конгруенцій з модулями, що ґрунтуються на тому самому простому числі, але, можливо, з різними степенями цього простого.

Можна помітити, що конгруенція з модулем, який є найвищим степенем простого, буде найсильнішою серед усіх конгруенцій, що ґрунтуються на тому самому простому числі. Вона або даватиме суперечність з якоюсь іншою конгруенцією, або вже випливатиме з неї всі інші конгруенції.

У нашому випадку перша конгруенція a1(mod4)a \equiv 1 \pmod{4} випливає a1(mod2)a \equiv 1 \pmod{2}, а тому суперечить другій конгруенції a0(mod2)a \equiv 0 \pmod{2}. Отже, ця система конгруенцій не має розв'язку.

Якщо суперечностей немає, то система рівнянь має розв'язок. Ми можемо знехтувати всіма конгруенціями, крім тих, у яких модулі є найвищими степенями простих. Тепер ці модулі взаємно прості, а тому ми можемо розв'язати таку систему за допомогою алгоритму, обговореного в попередніх розділах.

Наприклад, наведена нижче система має розв'язок за модулем lcm(10,12)=60\text{lcm}(10, 12) = 60.

{a3(mod10)a5(mod12)\left\{\begin{align} a & \equiv 3 \pmod{10} \\ a & \equiv 5 \pmod{12} \end{align}\right.

Ця система конгруенцій еквівалентна системі конгруенцій:

{a31(mod2)a33(mod5)a51(mod4)a52(mod3)\left\{\begin{align} a & \equiv 3 \equiv 1 \pmod{2} \\ a & \equiv 3 \equiv 3 \pmod{5} \\ a & \equiv 5 \equiv 1 \pmod{4} \\ a & \equiv 5 \equiv 2 \pmod{3} \end{align}\right.

Єдині конгруенції з тим самим простим модулем — це a1(mod4)a \equiv 1 \pmod{4} та a1(mod2)a \equiv 1 \pmod{2}. Перша вже випливає другу, тож ми можемо знехтувати другою і натомість розв'язати таку систему зі взаємно простими модулями:

{a33(mod5)a51(mod4)a52(mod3)\left\{\begin{align} a & \equiv 3 \equiv 3 \pmod{5} \\ a & \equiv 5 \equiv 1 \pmod{4} \\ a & \equiv 5 \equiv 2 \pmod{3} \end{align}\right.

Вона має розв'язок 53(mod60)53 \pmod{60}, і справді 53mod10=353 \bmod{10} = 3 та 53mod12=553 \bmod{12} = 5.

Алгоритм Гарнера

Ще одним наслідком CRT є те, що ми можемо подавати великі числа за допомогою масиву невеликих цілих.

Замість того, щоб виконувати багато обчислень з дуже великими числами, що може бути дорого (уявіть собі ділення з 1000-значними числами), ви можете обрати кілька взаємно простих модулів, подати велике число як систему конгруенцій і виконувати всі операції над цією системою рівнянь. Будь-яке число aa, менше за m1m2mkm_1 m_2 \cdots m_k, можна подати як масив a1,,aka_1, \ldots, a_k, де aai(modmi)a \equiv a_i \pmod{m_i}.

Скориставшись наведеним вище алгоритмом, ви можете будь-коли знову відновити велике число, коли воно вам потрібне.

Як альтернатива, ви можете подати число у вигляді мішаної системи числення (mixed radix):

a=x1+x2m1+x3m1m2++xkm1mk1 with xi[0,mi)a = x_1 + x_2 m_1 + x_3 m_1 m_2 + \ldots + x_k m_1 \cdots m_{k-1} \text{ with }x_i \in [0, m_i)

Алгоритм Гарнера, який обговорюється в окремій статті Алгоритм Гарнера, обчислює коефіцієнти xix_i. А з цими коефіцієнтами ви можете відновити повне число.

Задачі для практики:

Відеоматеріали