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

Ланцюгові дроби

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

Окрім того, ланцюгові дроби тісно пов'язані з алгоритмом Евкліда, що робить їх корисними в низці задач теорії чисел.

Представлення ланцюговим дробом

Означення

Нехай a0,a1,,akZa_0, a_1, \dots, a_k \in \mathbb Z і a1,a2,,ak1a_1, a_2, \dots, a_k \geq 1. Тоді вираз

r=a0+1a1+1+1ak,r=a_0 + \frac{1}{a_1 + \frac{1}{\dots + \frac{1}{a_k}}},

називається представленням раціонального числа rr ланцюговим дробом і коротко позначається як r=[a0;a1,a2,,ak]r=[a_0;a_1,a_2,\dots,a_k].

Приклад

Нехай r=53r = \frac{5}{3}. Існує два способи представити його у вигляді ланцюгового дробу:

r=[1;1,1,1]=1+11+11+11,r=[1;1,2]=1+11+12.\begin{align} r = [1;1,1,1] &= 1+\frac{1}{1+\frac{1}{1+\frac{1}{1}}},\\ r = [1;1,2] &= 1+\frac{1}{1+\frac{1}{2}}. \end{align}

Можна довести, що будь-яке раціональне число можна представити ланцюговим дробом рівно 22 способами:

r=[a0;a1,,ak,1]=[a0;a1,,ak+1].r = [a_0;a_1,\dots,a_k,1] = [a_0;a_1,\dots,a_k+1].

Понад те, довжина kk такого ланцюгового дробу оцінюється як k=O(logmin(p,q))k = O(\log \min(p, q)) для r=pqr=\frac{p}{q}.

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

Означення

Нехай a0,a1,a2,a_0,a_1,a_2, \dots — цілочислова послідовність така, що a1,a2,1a_1, a_2, \dots \geq 1. Нехай rk=[a0;a1,,ak]r_k = [a_0; a_1, \dots, a_k]. Тоді вираз

r=a0+1a1+1a2+=limkrk.r = a_0 + \frac{1}{a_1 + \frac{1}{a_2+\dots}} = \lim\limits_{k \to \infty} r_k.

називається представленням ірраціонального числа rr ланцюговим дробом і коротко позначається як r=[a0;a1,a2,]r = [a_0;a_1,a_2,\dots].

Зауважимо, що для r=[a0;a1,]r=[a_0;a_1,\dots] і цілого kk виконується r+k=[a0+k;a1,]r+k = [a_0+k; a_1, \dots].

Інше важливе спостереження: 1r=[0;a0,a1,]\frac{1}{r}=[0;a_0, a_1, \dots] коли a0>0a_0 > 0 і 1r=[a1;a2,]\frac{1}{r} = [a_1; a_2, \dots] коли a0=0a_0 = 0.

Означення

У наведеному вище означенні раціональні числа r0,r1,r2,r_0, r_1, r_2, \dots називаються підхідними дробами числа rr.

Відповідно, окремий rk=[a0;a1,,ak]=pkqkr_k = [a_0; a_1, \dots, a_k] = \frac{p_k}{q_k} називається kkпідхідним дробом числа rr.

Приклад

Розглянемо r=[1;1,1,1,]r = [1; 1, 1, 1, \dots]. Індукцією можна довести, що rk=Fk+2Fk+1r_k = \frac{F_{k+2}}{F_{k+1}}, де FkF_k — послідовність Фібоначчі, визначена як F0=0F_0 = 0, F1=1F_1 = 1 і Fk=Fk1+Fk2F_{k} = F_{k-1} + F_{k-2}. З формули Біне відомо, що

rk=ϕk+2ψk+2ϕk+1ψk+1,r_k = \frac{\phi^{k+2} - \psi^{k+2}}{\phi^{k+1} - \psi^{k+1}},

де ϕ=1+521.618\phi = \frac{1+\sqrt{5}}{2} \approx 1.618 — золотий перетин, а ψ=152=1ϕ0.618\psi = \frac{1-\sqrt{5}}{2} = -\frac{1}{\phi} \approx -0.618. Отже,

r=1+11+11+=limkrk=ϕ=1+52.r = 1+\frac{1}{1+\frac{1}{1+\dots}}=\lim\limits_{k \to \infty} r_k = \phi = \frac{1+\sqrt{5}}{2}.

Зауважимо, що в цьому конкретному випадку альтернативним способом знайти rr було б розв'язати рівняння

r=1+1r    r2=r+1.r = 1+\frac{1}{r} \implies r^2 = r + 1.
Означення

Нехай rk=[a0;a1,,ak1,ak]r_k = [a_0; a_1, \dots, a_{k-1}, a_k]. Числа [a0;a1,,ak1,t][a_0; a_1, \dots, a_{k-1}, t] для 1tak1 \leq t \leq a_k називаються напівпідхідними дробами.

Зазвичай ми називатимемо (напів)підхідні дроби, що більші за rr, верхніми (напів)підхідними дробами, а ті, що менші за rr, — нижніми (напів)підхідними дробами.

Означення

На додаток до підхідних дробів, означимо повні частки як sk=[ak;ak+1,ak+2,]s_k = [a_k; a_{k+1}, a_{k+2}, \dots].

Відповідно, окремий sks_k ми називатимемо kk-ю повною часткою числа rr.

З наведених вище означень можна зробити висновок, що sk1s_k \geq 1 для k1k \geq 1.

Розглядаючи [a0;a1,,ak][a_0; a_1, \dots, a_k] як формальний алгебраїчний вираз і дозволяючи довільні дійсні числа замість aia_i, ми отримуємо

r=[a0;a1,,ak1,sk].r = [a_0; a_1, \dots, a_{k-1}, s_k].

Зокрема, r=[s0]=s0r = [s_0] = s_0. З іншого боку, ми можемо виразити sks_k як

sk=[ak;sk+1]=ak+1sk+1,s_k = [a_k; s_{k+1}] = a_k + \frac{1}{s_{k+1}},

що означає, що ми можемо обчислити ak=ska_k = \lfloor s_k \rfloor і sk+1=(skak)1s_{k+1} = (s_k - a_k)^{-1} з sks_k.

Послідовність a0,a1,a_0, a_1, \dots визначена коректно, доки skaks_k \neq a_k, що трапляється лише тоді, коли rr — раціональне число.

Отже, представлення ланцюговим дробом однозначно визначене для будь-якого ірраціонального числа rr.

Реалізація

У фрагментах коду ми здебільшого вважатимемо ланцюгові дроби скінченними.

З sks_k перехід до sk+1s_{k+1} виглядає так:

sk=sk+1sk+1.s_k =\left\lfloor s_k \right\rfloor + \frac{1}{s_{k+1}}.

З цього виразу наступна повна частка sk+1s_{k+1} отримується як

sk+1=(sksk)1.s_{k+1} = \left(s_k-\left\lfloor s_k\right\rfloor\right)^{-1}.

Для sk=pqs_k=\frac{p}{q} це означає, що

sk+1=(pqpq)1=qpqpq=qpmodq.s_{k+1} = \left(\frac{p}{q}-\left\lfloor \frac{p}{q} \right\rfloor\right)^{-1} = \frac{q}{p-q\cdot \lfloor \frac{p}{q} \rfloor} = \frac{q}{p \bmod q}.

Отже, обчислення представлення ланцюговим дробом для r=pqr=\frac{p}{q} повторює кроки алгоритму Евкліда для pp і qq.

З цього також випливає, що gcd(pk,qk)=1\gcd(p_k, q_k) = 1 для pkqk=[a0;a1,,ak]\frac{p_k}{q_k} = [a_0; a_1, \dots, a_k]. Отже, підхідні дроби завжди нескоротні.

auto fraction(int p, int q) {
vector<int> a;
while(q) {
a.push_back(p / q);
tie(p, q) = make_pair(q, p % q);
}
return a;
}

Ключові результати

Щоб дати певну мотивацію для подальшого вивчення ланцюгових дробів, наведемо зараз кілька ключових фактів.

Рекурентне співвідношення

Для підхідних дробів rk=pkqkr_k = \frac{p_k}{q_k} виконується таке рекурентне співвідношення, що дозволяє їх швидко обчислювати:

pkqk=akpk1+pk2akqk1+qk2,\frac{p_k}{q_k}=\frac{a_k p_{k-1} + p_{k-2}}{a_k q_{k-1} + q_{k-2}},

де p1q1=10\frac{p_{-1}}{q_{-1}}=\frac{1}{0} і p2q2=01\frac{p_{-2}}{q_{-2}}=\frac{0}{1}.

Відхилення

Відхилення rk=pkqkr_k = \frac{p_k}{q_k} від rr загалом можна оцінити як

pkqkr1qkqk+11qk2.\left|\frac{p_k}{q_k}-r\right| \leq \frac{1}{q_k q_{k+1}} \leq \frac{1}{q_k^2}.

Помноживши обидві частини на qkq_k, отримуємо альтернативну оцінку:

pkqkr1qk+1.|p_k - q_k r| \leq \frac{1}{q_{k+1}}.

З наведеного вище рекурентного співвідношення випливає, що qkq_k зростає принаймні так само швидко, як числа Фібоначчі.

На малюнку нижче ви можете побачити візуалізацію того, як підхідні дроби rkr_k наближаються до r=1+52r=\frac{1+\sqrt 5}{2}:

r=1+52r=\frac{1+\sqrt 5}{2} зображено синьою пунктирною лінією. Непарні підхідні дроби наближаються до нього зверху, а парні — знизу.

Ґратчасті оболонки

Розглянемо опуклі оболонки точок над і під прямою y=rxy=rx.

Непарні підхідні дроби (qk;pk)(q_k;p_k) є вершинами верхньої оболонки, тоді як парні підхідні дроби (qk;pk)(q_k;p_k) є вершинами нижньої оболонки.

Усі цілочислові вершини на оболонках отримуються як (q;p)(q;p) такі, що

pq=tpk1+pk2tqk1+qk2\frac{p}{q} = \frac{tp_{k-1} + p_{k-2}}{tq_{k-1} + q_{k-2}}

для цілого 0tak0 \leq t \leq a_k. Іншими словами, множина ґратчастих точок на оболонках відповідає множині напівпідхідних дробів.

На малюнку нижче ви можете побачити підхідні дроби та напівпідхідні дроби (проміжні сірі точки) числа r=97r=\frac{9}{7}.

Найкращі наближення

Нехай pq\frac{p}{q} — дріб, що мінімізує rpq\left|r-\frac{p}{q}\right| за умови qxq \leq x для деякого xx.

Тоді pq\frac{p}{q} є напівпідхідним дробом числа rr.

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

Нижче ви знайдете подальше пояснення, а також трохи інтуїції та інтерпретації цих фактів.

Підхідні дроби

Розгляньмо детальніше підхідні дроби, означені раніше. Для r=[a0,a1,a2,]r=[a_0, a_1, a_2, \dots] його підхідними дробами є

\begin{gather} r_0=[a_0],\r_1=[a_0, a_1],\ \dots,\ r_k=[a_0, a_1, \dots, a_k]. \end{gather}

Підхідні дроби — основне поняття ланцюгових дробів, тож важливо вивчити їхні властивості.

Для числа rr його kk-й підхідний дріб rk=pkqkr_k = \frac{p_k}{q_k} можна обчислити як

rk=Pk(a0,a1,,ak)Pk1(a1,,ak)=akpk1+pk2akqk1+qk2,r_k = \frac{P_k(a_0,a_1,\dots,a_k)}{P_{k-1}(a_1,\dots,a_k)} = \frac{a_k p_{k-1} + p_{k-2}}{a_k q_{k-1} + q_{k-2}},

де Pk(a0,,ak)P_k(a_0,\dots,a_k)континуанта, багатовимірний многочлен, означений як

Pk(x0,x1,,xk)=det[xk1001xk11001x2..1001x0].P_k(x_0,x_1,\dots,x_k) = \det \begin{bmatrix} x_k & 1 & 0 & \dots & 0 \\ -1 & x_{k-1} & 1 & \dots & 0 \\ 0 & -1 & x_2 & . & \vdots \\ \vdots & \vdots & . & \ddots & 1 \\ 0 & 0 & \dots & -1 & x_0 \end{bmatrix}_{\textstyle .}

Отже, rkr_k є зваженим медіантом дробів rk1r_{k-1} і rk2r_{k-2}.

Для узгодженості означають два додаткові підхідні дроби r1=10r_{-1} = \frac{1}{0} і r2=01r_{-2} = \frac{0}{1}.

Детальне пояснення

Чисельник і знаменник rkr_k можна розглядати як багатовимірні многочлени від a0,a1,,aka_0, a_1, \dots, a_k:

rk=Pk(a0,a1,,ak)Qk(a0,a1,,ak).r_k = \frac{P_k(a_0, a_1, \dots, a_k)}{Q_k(a_0,a_1, \dots, a_k)}.

З означення підхідних дробів,

rk=a0+1[a1;a2,,ak]=a0+Qk1(a1,,ak)Pk1(a1,,ak)=a0Pk1(a1,,ak)+Qk1(a1,,ak)Pk1(a1,,ak).r_k = a_0 + \frac{1}{[a_1;a_2,\dots, a_k]}= a_0 + \frac{Q_{k-1}(a_1, \dots, a_k)}{P_{k-1}(a_1, \dots, a_k)} = \frac{a_0 P_{k-1}(a_1, \dots, a_k) + Q_{k-1}(a_1, \dots, a_k)}{P_{k-1}(a_1, \dots, a_k)}.

Звідси випливає Qk(a0,,ak)=Pk1(a1,,ak)Q_k(a_0, \dots, a_k) = P_{k-1}(a_1, \dots, a_k). Це дає співвідношення

Pk(a0,,ak)=a0Pk1(a1,,ak)+Pk2(a2,,ak).P_k(a_0, \dots, a_k) = a_0 P_{k-1}(a_1, \dots, a_k) + P_{k-2}(a_2, \dots, a_k).

Спочатку r0=a01r_0 = \frac{a_0}{1} і r1=a0a1+1a1r_1 = \frac{a_0 a_1 + 1}{a_1}, тож

P0(a0)=a0,P1(a0,a1)=a0a1+1.\begin{align}P_0(a_0)&=a_0,\\ P_1(a_0, a_1) &= a_0 a_1 + 1.\end{align}

Для узгодженості зручно означити P1=1P_{-1} = 1 і P2=0P_{-2}=0 та формально вважати, що r1=10r_{-1} = \frac{1}{0} і r2=01r_{-2}=\frac{0}{1}.

З чисельного аналізу відомо, що визначник довільної тридіагональної матриці

Tk=det[a0b000c0a1b100c1a2..ck100bk1ak]T_k = \det \begin{bmatrix} a_0 & b_0 & 0 & \dots & 0 \\ c_0 & a_1 & b_1 & \dots & 0 \\ 0 & c_1 & a_2 & . & \vdots \\ \vdots & \vdots & . & \ddots & c_{k-1} \\ 0 & 0 & \dots & b_{k-1} & a_k \end{bmatrix}

можна обчислити рекурсивно як Tk=akTk1bk1ck1Tk2T_k = a_k T_{k-1} - b_{k-1} c_{k-1} T_{k-2}. Порівнюючи його з PkP_k, отримуємо прямий вираз

Pk=det[xk1001xk11001x2..1001x0].P_k = \det \begin{bmatrix} x_k & 1 & 0 & \dots & 0 \\ -1 & x_{k-1} & 1 & \dots & 0 \\ 0 & -1 & x_2 & . & \vdots \\ \vdots & \vdots & . & \ddots & 1 \\ 0 & 0 & \dots & -1 & x_0 \end{bmatrix}_{\textstyle .}

Цей многочлен також відомий як континуанта через його тісний зв'язок із ланцюговим дробом. Континуанта не зміниться, якщо послідовність на головній діагоналі обернути. Це дає альтернативну формулу для її обчислення:

Pk(a0,,ak)=akPk1(a0,,ak1)+Pk2(a0,,ak2).P_k(a_0, \dots, a_k) = a_k P_{k-1}(a_0, \dots, a_{k-1}) + P_{k-2}(a_0, \dots, a_{k-2}).

Реалізація

Ми обчислюватимемо підхідні дроби як пару послідовностей p2,p1,p0,p1,,pkp_{-2}, p_{-1}, p_0, p_1, \dots, p_k і q2,q1,q0,q1,,qkq_{-2}, q_{-1}, q_0, q_1, \dots, q_k:

auto convergents(vector<int> a) {
vector<int> p = {0, 1};
vector<int> q = {1, 0};
for(auto it: a) {
p.push_back(p[p.size() - 1] * it + p[p.size() - 2]);
q.push_back(q[q.size() - 1] * it + q[q.size() - 2]);
}
return make_pair(p, q);
}

Дерева ланцюгових дробів

Існує два основні способи об'єднати всі можливі ланцюгові дроби в корисні деревоподібні структури.

Дерево Штерна–Броко

Дерево Штерна–Броко — це двійкове дерево пошуку, що містить усі різні додатні раціональні числа.

Дерево загалом виглядає так:

Зображення від Aaron Rotenberg ліцензовано під CC BY-SA 3.0

Дроби 01\frac{0}{1} і 10\frac{1}{0} "віртуально" зберігаються відповідно зліва і справа дерева.

Тоді дріб у вузлі є медіантом a+cb+d\frac{a+c}{b+d} двох дробів ab\frac{a}{b} і cd\frac{c}{d} над ним.

Рекурентне співвідношення pkqk=akpk1+pk2akqk1+qk2\frac{p_k}{q_k}=\frac{a_k p_{k-1} + p_{k-2}}{a_k q_{k-1} + q_{k-2}} означає, що представлення ланцюговим дробом кодує шлях до pkqk\frac{p_k}{q_k} у дереві. Щоб знайти [a0;a1,,ak,1][a_0; a_1, \dots, a_{k}, 1], треба зробити a0a_0 кроків праворуч, a1a_1 кроків ліворуч, a2a_2 кроків праворуч і так далі аж до aka_k.

Батьком [a0;a1,,ak,1][a_0; a_1, \dots, a_k,1] тоді є дріб, отриманий кроком назад у напрямку, що використовувався останнім.

Іншими словами, це [a0;a1,,ak1,1][a_0; a_1, \dots, a_k-1,1] коли ak>1a_k > 1 і [a0;a1,,ak1,1][a_0; a_1, \dots, a_{k-1}, 1] коли ak=1a_k = 1.

Отже, дітьми [a0;a1,,ak,1][a_0; a_1, \dots, a_k, 1] є [a0;a1,,ak+1,1][a_0; a_1, \dots, a_k+1, 1] і [a0;a1,,ak,1,1][a_0; a_1, \dots, a_k, 1, 1].

Проіндексуймо дерево Штерна–Броко. Кореневій вершині присвоюється індекс 11. Тоді для вершини vv індекс її лівої дитини присвоюється зміною провідного біта vv з 11 на 1010, а для правої дитини він присвоюється зміною провідного біта з 11 на 1111:

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

Для 52=[2;2]=[2;1,1]\frac{5}{2} = [2;2] = [2;1,1] його індекс дорівнює 101121011_2, а його кодування довжин серій, якщо розглядати біти у порядку зростання, дорівнює [2;1,1][2;1,1].

Інший приклад — 25=[0;2,2]=[0;2,1,1]\frac{2}{5} = [0;2,2]=[0;2,1,1], який має індекс 110021100_2, а його кодування довжин серій справді дорівнює [0;2,2][0;2,2].

Варто зазначити, що дерево Штерна–Броко насправді є декартовим деревом. Тобто це двійкове дерево пошуку за pq\frac{p}{q}, але це купа і за pp, і за qq.

Порівняння ланцюгових дробів

Вам дано A=[a0;a1,,an]A=[a_0; a_1, \dots, a_n] і B=[b0;b1,,bm]B=[b_0; b_1, \dots, b_m]. Який дріб менший?

Розв'язок

Припустимо поки що, що AA і BB ірраціональні, а їхні представлення ланцюговими дробами задають нескінченний спуск у дереві Штерна–Броко.

Як ми вже згадували, у цьому представленні a0a_0 позначає кількість поворотів праворуч у спуску, a1a_1 позначає кількість послідовних поворотів ліворуч і так далі. Тому, коли ми порівнюємо aka_k і bkb_k, якщо ak=bka_k = b_k, ми просто переходимо до порівняння ak+1a_{k+1} і bk+1b_{k+1}. Інакше, якщо ми на правих спусках, нам слід перевірити, чи ak<bka_k < b_k, а якщо ми на лівих спусках, нам слід перевірити, чи ak>bka_k > b_k, щоб визначити, чи A<BA < B.

Іншими словами, для ірраціональних AA і BB буде A<BA < B тоді й лише тоді, коли (a0,a1,a2,a3,)<(b0,b1,b2,b3,)(a_0, -a_1, a_2, -a_3, \dots) < (b_0, -b_1, b_2, -b_3, \dots) при лексикографічному порівнянні.

Тепер, формально використовуючи \infty як елемент представлення ланцюговим дробом, можна емулювати ірраціональні числа AεA-\varepsilon і A+εA+\varepsilon, тобто елементи, які менші (більші) за AA, але більші (менші) за будь-яке інше дійсне число. Зокрема, для A=[a0;a1,,an]A=[a_0; a_1, \dots, a_n] один із цих двох елементів можна емулювати як [a0;a1,,an,][a_0; a_1, \dots, a_n, \infty], а інший — як [a0;a1,,an1,1,][a_0; a_1, \dots, a_n - 1, 1, \infty].

Який із них відповідає AεA-\varepsilon, а який — A+εA+\varepsilon, можна визначити за парністю nn або порівнявши їх як ірраціональні числа.

# перевіряємо, чи a < b, припускаючи, що a[-1] = b[-1] = infty і a != b
def less(a, b):
a = [(-1)**i*a[i] for i in range(len(a))]
b = [(-1)**i*b[i] for i in range(len(b))]
return a < b

# [a0; a1, ..., ak] -> [a0, a1, ..., ak-1, 1]
def expand(a):
if a: # порожнє a = inf
a[-1] -= 1
a.append(1)
return a

# повертає a-eps, a+eps
def pm_eps(a):
b = expand(a.copy())
a.append(float('inf'))
b.append(float('inf'))
return (a, b) if less(a, b) else (b, a)
Найкраща внутрішня точка

Вам дано 01p0q0<p1q110\frac{0}{1} \leq \frac{p_0}{q_0} < \frac{p_1}{q_1} \leq \frac{1}{0}. Знайдіть раціональне число pq\frac{p}{q} таке, що (q;p)(q; p) є лексикографічно найменшим і p0q0<pq<p1q1\frac{p_0}{q_0} < \frac{p}{q} < \frac{p_1}{q_1}.

Розв'язок

У термінах дерева Штерна–Броко це означає, що нам потрібно знайти LCA дробів p0q0\frac{p_0}{q_0} і p1q1\frac{p_1}{q_1}. Завдяки зв'язку між деревом Штерна–Броко і ланцюговим дробом цей LCA приблизно відповідатиме найбільшому спільному префіксу представлень ланцюговими дробами для p0q0\frac{p_0}{q_0} і p1q1\frac{p_1}{q_1}.

Отже, якщо p0q0=[a0;a1,,ak1,ak,]\frac{p_0}{q_0} = [a_0; a_1, \dots, a_{k-1}, a_k, \dots] і p1q1=[a0;a1,,ak1,bk,]\frac{p_1}{q_1} = [a_0; a_1, \dots, a_{k-1}, b_k, \dots] — ірраціональні числа, то LCA дорівнює [a0;a1,,min(ak,bk)+1][a_0; a_1, \dots, \min(a_k, b_k)+1].

Для раціональних r0r_0 і r1r_1 один із них може бути самим LCA, що змусило б нас розбирати випадки. Щоб спростити розв'язок для раціональних r0r_0 і r1r_1, можна використати представлення ланцюговими дробами чисел r0+εr_0 + \varepsilon і r1εr_1 - \varepsilon, яке було виведене в попередній задачі.

# знаходить лексикографічно найменше (q, p)
# таке, що p0/q0 < p/q < p1/q1
def middle(p0, q0, p1, q1):
a0 = pm_eps(fraction(p0, q0))[1]
a1 = pm_eps(fraction(p1, q1))[0]
a = []
for i in range(min(len(a0), len(a1))):
a.append(min(a0[i], a1[i]))
if a0[i] != a1[i]:
break
a[-1] += 1
p, q = convergents(a)
return p[-1], q[-1]
інформація

GCJ 2019, Round 2 - New Elements: Part 2

Вам дано NN пар додатних цілих чисел (Ci,Ji)(C_i, J_i). Потрібно знайти пару додатних цілих чисел (x,y)(x, y) таку, що Cix+JiyC_i x + J_i y є строго зростаючою послідовністю.

Серед таких пар знайдіть лексикографічно мінімальну.

Розв'язок

Переформулюємо умову: Aix+BiyA_i x + B_i y має бути додатним для всіх ii, де Ai=CiCi1A_i = C_i - C_{i-1} і Bi=JiJi1B_i = J_i - J_{i-1}.

Серед таких рівнянь маємо чотири значущі групи для Aix+Biy>0A_i x + B_i y > 0:

  1. Ai,Bi>0A_i, B_i > 0 можна ігнорувати, оскільки ми шукаємо x,y>0x, y > 0.
  2. Ai,Bi0A_i, B_i \leq 0 давало б "IMPOSSIBLE" як відповідь.
  3. Ai>0A_i > 0, Bi0B_i \leq 0. Такі обмеження еквівалентні yx<AiBi\frac{y}{x} < \frac{A_i}{-B_i}.
  4. Ai0A_i \leq 0, Bi>0B_i > 0. Такі обмеження еквівалентні yx>AiBi\frac{y}{x} > \frac{-A_i}{B_i}.

Нехай p0q0\frac{p_0}{q_0} — найбільший AiBi\frac{-A_i}{B_i} з четвертої групи, а p1q1\frac{p_1}{q_1} — найменший AiBi\frac{A_i}{-B_i} з третьої групи.

Тепер задача така: за даними p0q0<p1q1\frac{p_0}{q_0} < \frac{p_1}{q_1} знайти дріб pq\frac{p}{q} такий, що (q;p)(q;p) лексикографічно найменший і p0q0<pq<p1q1\frac{p_0}{q_0} < \frac{p}{q} < \frac{p_1}{q_1}.

def solve():
n = int(input())
C = [0] * n
J = [0] * n
# p0/q0 < y/x < p1/q1
p0, q0 = 0, 1
p1, q1 = 1, 0
fail = False
for i in range(n):
C[i], J[i] = map(int, input().split())
if i > 0:
A = C[i] - C[i-1]
B = J[i] - J[i-1]
if A <= 0 and B <= 0:
fail = True
elif B > 0 and A < 0: # y/x > (-A)/B if B > 0
if (-A)*q0 > p0*B:
p0, q0 = -A, B
elif B < 0 and A > 0: # y/x < A/(-B) if B < 0
if A*q1 < p1*(-B):
p1, q1 = A, -B
if p0*q1 >= p1*q0 or fail:
return 'IMPOSSIBLE'

p, q = middle(p0, q0, p1, q1)
return str(q) + ' ' + str(p)

Дерево Калкіна–Вілфа

Дещо простіший спосіб організувати ланцюгові дроби в двійкове дерево — це дерево Калкіна–Вілфа.

Дерево загалом виглядає так:

Зображення від Olli Niemitalo, Proz ліцензовано під CC0 1.0

У корені дерева розташоване число 11\frac{1}{1}. Тоді для вершини з числом pq\frac{p}{q} її дітьми є pp+q\frac{p}{p+q} і p+qq\frac{p+q}{q}.

На відміну від дерева Штерна–Броко, дерево Калкіна–Вілфа не є двійковим деревом пошуку, тож його не можна використовувати для виконання раціонального бінарного пошуку.

У дереві Калкіна–Вілфа безпосереднім батьком дробу pq\frac{p}{q} є pqq\frac{p-q}{q} коли p>qp>q і pqp\frac{p}{q-p} в іншому випадку.

Для дерева Штерна–Броко ми використовували рекурентне співвідношення для підхідних дробів. Щоб провести зв'язок між ланцюговим дробом і деревом Калкіна–Вілфа, нам слід пригадати рекурентне співвідношення для повних часток. Якщо sk=pqs_k = \frac{p}{q}, то sk+1=qpmodq=qpp/qqs_{k+1} = \frac{q}{p \mod q} = \frac{q}{p-\lfloor p/q \rfloor \cdot q}.

З іншого боку, якщо ми багаторазово переходимо від sk=pqs_k = \frac{p}{q} до його батька в дереві Калкіна–Вілфа коли p>qp > q, ми опинимося в pmodqq=1sk+1\frac{p \mod q}{q} = \frac{1}{s_{k+1}}. Якщо продовжувати так робити, ми опинимося в sk+2s_{k+2}, потім 1sk+3\frac{1}{s_{k+3}} і так далі. Звідси можемо вивести, що:

  1. Коли a0>0a_0> 0, безпосереднім батьком [a0;a1,,ak][a_0; a_1, \dots, a_k] у дереві Калкіна–Вілфа є pqq=[a01;a1,,ak]\frac{p-q}{q}=[a_0 - 1; a_1, \dots, a_k].
  2. Коли a0=0a_0 = 0 і a1>1a_1 > 1, його безпосереднім батьком є pqp=[0;a11,a2,,ak]\frac{p}{q-p} = [0; a_1 - 1, a_2, \dots, a_k].
  3. А коли a0=0a_0 = 0 і a1=1a_1 = 1, його безпосереднім батьком є pqp=[a2;a3,,ak]\frac{p}{q-p} = [a_2; a_3, \dots, a_k].

Відповідно, дітьми pq=[a0;a1,,ak]\frac{p}{q} = [a_0; a_1, \dots, a_k] є

  1. p+qq=1+pq\frac{p+q}{q}=1+\frac{p}{q}, що є [a0+1;a1,,ak][a_0+1; a_1, \dots, a_k],
  2. pp+q=11+qp\frac{p}{p+q} = \frac{1}{1+\frac{q}{p}}, що є [0,1,a0,a1,,ak][0, 1, a_0, a_1, \dots, a_k] для a0>0a_0 > 0 і [0,a1+1,a2,,ak][0, a_1+1, a_2, \dots, a_k] для a0=0a_0=0.

Варто зауважити, що якщо ми пронумеруємо вершини дерева Калкіна–Вілфа в порядку пошуку в ширину (тобто корінь має номер 11, а діти вершини vv мають індекси 2v2v і 2v+12v+1 відповідно), то індекс раціонального числа в дереві Калкіна–Вілфа буде таким самим, як у дереві Штерна–Броко.

Отже, числа на однакових рівнях дерева Штерна–Броко і дерева Калкіна–Вілфа однакові, але їхній порядок різниться через перестановку з обертанням бітів.

Збіжність

Для числа rr і його kk-го підхідного дробу rk=pkqkr_k=\frac{p_k}{q_k} виконується така формула:

rk=a0+i=1k(1)i1qiqi1.r_k = a_0 + \sum\limits_{i=1}^k \frac{(-1)^{i-1}}{q_i q_{i-1}}.

Зокрема, це означає, що

rkrk1=(1)k1qkqk1r_k - r_{k-1} = \frac{(-1)^{k-1}}{q_k q_{k-1}}

і

pkqk1pk1qk=(1)k1.p_k q_{k-1} - p_{k-1} q_k = (-1)^{k-1}.

Звідси можемо зробити висновок, що

rpkqk1qk+1qk1qk2.\left| r-\frac{p_k}{q_k} \right| \leq \frac{1}{q_{k+1}q_k} \leq \frac{1}{q_k^2}.

Остання нерівність зумовлена тим, що rkr_k і rk+1r_{k+1} загалом розташовані по різні боки від rr, тож

rrk=rkrk+1rrk+1rkrk+1.|r-r_k| = |r_k-r_{k+1}|-|r-r_{k+1}| \leq |r_k - r_{k+1}|.
Детальне пояснення

Щоб оцінити rrk|r-r_k|, почнемо з оцінки різниці між сусідніми підхідними дробами. За означенням,

pkqkpk1qk1=pkqk1pk1qkqkqk1.\frac{p_k}{q_k} - \frac{p_{k-1}}{q_{k-1}} = \frac{p_k q_{k-1} - p_{k-1} q_k}{q_k q_{k-1}}.

Замінивши pkp_k і qkq_k у чисельнику їхніми рекурентними співвідношеннями, отримуємо

pkqk1pk1qk=(akpk1+pk2)qk1pk1(akqk1+qk2)=pk2qk1pk1qk2,\begin{align} p_k q_{k-1} - p_{k-1} q_k &= (a_k p_{k-1} + p_{k-2}) q_{k-1} - p_{k-1} (a_k q_{k-1} + q_{k-2}) \\&= p_{k-2} q_{k-1} - p_{k-1} q_{k-2},\end{align}

тож чисельник rkrk1r_k - r_{k-1} завжди є взятим зі знаком мінус чисельником rk1rk2r_{k-1} - r_{k-2}. Він, своєю чергою, дорівнює 11 для

r1r0=(a0+1a1)a0=1a1,r_1 - r_0=\left(a_0+\frac{1}{a_1}\right)-a_0=\frac{1}{a_1},

тож

rkrk1=(1)k1qkqk1.r_k - r_{k-1} = \frac{(-1)^{k-1}}{q_k q_{k-1}}.

Це дає альтернативне представлення rkr_k як часткової суми нескінченного ряду:

rk=(rkrk1)++(r1r0)+r0=a0+i=1k(1)i1qiqi1.r_k = (r_k - r_{k-1}) + \dots + (r_1 - r_0) + r_0 = a_0 + \sum\limits_{i=1}^k \frac{(-1)^{i-1}}{q_i q_{i-1}}.

З рекурентного співвідношення випливає, що qkq_k монотонно зростає принаймні так само швидко, як числа Фібоначчі, тож

r=limkrk=a0+i=1(1)i1qiqi1r = \lim\limits_{k \to \infty} r_k = a_0 + \sum\limits_{i=1}^\infty \frac{(-1)^{i-1}}{q_i q_{i-1}}

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

rrk=i=k+1(1)i1qiqi1r-r_k = \sum\limits_{i=k+1}^\infty \frac{(-1)^{i-1}}{q_i q_{i-1}}

має той самий знак, що й (1)k(-1)^k, через те, наскільки швидко спадає qiqi1q_i q_{i-1}. Отже, rkr_k з парними індексами наближаються до rr знизу, тоді як rkr_k з непарними індексами наближаються до нього зверху:

Підхідні дроби r=ϕ=1+52=[1;1,1,]r=\phi = \frac{1+\sqrt{5}}{2}=[1;1,1,\dots] та їхня відстань від rr.

З цього малюнка ми бачимо, що

rrk=rkrk+1rrk+1rkrk+1,|r-r_k| = |r_k - r_{k+1}| - |r-r_{k+1}| \leq |r_k - r_{k+1}|,

тож відстань між rr і rkr_k ніколи не більша за відстань між rkr_k і rk+1r_{k+1}:

rpkqk1qkqk+11qk2.\left|r-\frac{p_k}{q_k}\right| \leq \frac{1}{q_k q_{k+1}} \leq \frac{1}{q_k^2}.
Розширений Евклід?

Вам дано A,B,CZA, B, C \in \mathbb Z. Знайдіть x,yZx, y \in \mathbb Z такі, що Ax+By=CAx + By = C.

Розв'язок

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

Нехай AB=[a0;a1,,ak]\frac{A}{B}=[a_0; a_1, \dots, a_k]. Вище було доведено, що pkqk1pk1qk=(1)k1p_k q_{k-1} - p_{k-1} q_k = (-1)^{k-1}. Підставивши pkp_k і qkq_k замість AA і BB, отримуємо

Aqk1Bpk1=(1)k1g,Aq_{k-1} - Bp_{k-1} = (-1)^{k-1} g,

де g=gcd(A,B)g = \gcd(A, B). Якщо CC ділиться на gg, то розв'язком є x=(1)k1Cgqk1x = (-1)^{k-1}\frac{C}{g} q_{k-1} і y=(1)kCgpk1y = (-1)^{k}\frac{C}{g} p_{k-1}.

# повертає (x, y) таке, що Ax+By=C
# припускає, що таке (x, y) існує
def dio(A, B, C):
p, q = convergents(fraction(A, B))
C //= A // p[-1] # ділимо на gcd(A, B)
t = (-1) if len(p) % 2 else 1
return t*C*q[-2], -t*C*p[-2]

Дробово-лінійні перетворення

Ще одне важливе поняття для ланцюгових дробів — це так звані дробово-лінійні перетворення.

Означення

Дробово-лінійне перетворення — це функція f:RRf : \mathbb R \to \mathbb R така, що f(x)=ax+bcx+df(x) = \frac{ax+b}{cx+d} для деяких a,b,c,dRa,b,c,d \in \mathbb R.

Композиція (L0L1)(x)=L0(L1(x))(L_0 \circ L_1)(x) = L_0(L_1(x)) дробово-лінійних перетворень L0(x)=a0x+b0c0x+d0L_0(x)=\frac{a_0 x + b_0}{c_0 x + d_0} і L1(x)=a1x+b1c1x+d1L_1(x)=\frac{a_1 x + b_1}{c_1 x + d_1} сама є дробово-лінійним перетворенням:

a0a1x+b1c1x+d1+b0c0a1x+b1c1x+d1+d0=a0(a1x+b1)+b0(c1x+d1)c0(a1x+b1)+d0(c1x+d1)=(a0a1+b0c1)x+(a0b1+b0d1)(c0a1+d0c1)x+(c0b1+d0d1).\frac{a_0\frac{a_1 x + b_1}{c_1 x + d_1} + b_0}{c_0 \frac{a_1 x + b_1}{c_1 x + d_1} + d_0} = \frac{a_0(a_1 x + b_1) + b_0 (c_1 x + d_1)}{c_0 (a_1 x + b_1) + d_0 (c_1 x + d_1)} = \frac{(a_0 a_1 + b_0 c_1) x + (a_0 b_1 + b_0 d_1)}{(c_0 a_1 + d_0 c_1) x + (c_0 b_1 + d_0 d_1)}.

Обернене до дробово-лінійного перетворення також є дробово-лінійним перетворенням:

y=ax+bcx+d    y(cx+d)=ax+b    x=dybcya.y = \frac{ax+b}{cx+d} \iff y(cx+d) = ax + b \iff x = -\frac{dy-b}{cy-a}.
інформація

DMOPC '19 Contest 7 P4 - Bob and Continued Fractions

Вам дано масив додатних цілих чисел a1,,ana_1, \dots, a_n. Потрібно відповісти на mm запитів. Кожен запит — обчислити [al;al+1,,ar][a_l; a_{l+1}, \dots, a_r].

Розв'язок

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

Загалом справедливо, що [a0;a1,,ak,b0,b1,,bk]=[a0;a1,,ak,[b1;b2,,bk]][a_0; a_1, \dots, a_k, b_0, b_1, \dots, b_k] = [a_0; a_1, \dots, a_k, [b_1; b_2, \dots, b_k]].

Позначимо Lk(x)=[ak;x]=ak+1x=akx+11x+0L_{k}(x) = [a_k; x] = a_k + \frac{1}{x} = \frac{a_k\cdot x+1}{1\cdot x + 0}. Зауважимо, що Lk()=akL_k(\infty) = a_k. У цих позначеннях виконується

[a0;a1,,ak,x]=[a0;[a1;[;[ak;x]]]]=(L0L1Lk)(x)=pkx+pk1qkx+qk1.[a_0; a_1, \dots, a_k, x] = [a_0; [a_1; [\dots; [a_k; x]]]] = (L_0 \circ L_1 \circ \dots \circ L_k)(x) = \frac{p_k x + p_{k-1}}{q_k x + q_{k-1}}.

Отже, задача зводиться до обчислення

(LlLl+1Lr)().(L_l \circ L_{l+1} \circ \dots \circ L_r)(\infty).

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

Дробово-лінійне перетворення ланцюгового дробу

Нехай L(x)=ax+bcx+dL(x) = \frac{ax+b}{cx+d}. Обчисліть представлення ланцюговим дробом [b0;b1,,bm][b_0; b_1, \dots, b_m] числа L(A)L(A) для A=[a0;a1,,an]A=[a_0; a_1, \dots, a_n].

Це дозволяє обчислювати A+pq=qA+pqA + \frac{p}{q} = \frac{qA + p}{q} і Apq=pAqA \cdot \frac{p}{q} = \frac{p A}{q} для будь-якого pq\frac{p}{q}.

Розв'язок

Як ми зазначили вище, [a0;a1,,ak]=(La0La1Lak)()[a_0; a_1, \dots, a_k] = (L_{a_0} \circ L_{a_1} \circ \dots \circ L_{a_k})(\infty), тож L([a0;a1,,ak])=(LLa0La1Lak)()L([a_0; a_1, \dots, a_k]) = (L \circ L_{a_0} \circ L_{a_1} \circ \dots L_{a_k})(\infty).

Отже, послідовно додаючи La0L_{a_0}, La1L_{a_1} і так далі, ми зможемо обчислити

(LLa0Lak)(x)=L(pkx+pk1qkx+qk1)=akx+bkckx+dk.(L \circ L_{a_0} \circ \dots \circ L_{a_k})(x) = L\left(\frac{p_k x + p_{k-1}}{q_k x + q_{k-1}}\right)=\frac{a_k x + b_k}{c_k x + d_k}.

Оскільки L(x)L(x) оборотна, вона також монотонна за xx. Тому для будь-якого x0x \geq 0 виконується, що L(pkx+pk1qkx+qk1)L(\frac{p_k x + p_{k-1}}{q_k x + q_{k-1}}) лежить між L(pkqk)=akckL(\frac{p_k}{q_k}) = \frac{a_k}{c_k} і L(pk1qk1)=bkdkL(\frac{p_{k-1}}{q_{k-1}}) = \frac{b_k}{d_k}.

Понад те, для x=[ak+1;,an]x=[a_{k+1}; \dots, a_n] воно дорівнює L(A)L(A). Отже, b0=L(A)b_0 = \lfloor L(A) \rfloor лежить між L(pkqk)\lfloor L(\frac{p_k}{q_k}) \rfloor і L(pk1qk1)\lfloor L(\frac{p_{k-1}}{q_{k-1}}) \rfloor. Коли вони рівні, вони також рівні b0b_0.

Зауважимо, що L(A)=(Lb0Lb1Lbm)()L(A) = (L_{b_0} \circ L_{b_1} \circ \dots \circ L_{b_m})(\infty). Знаючи b0b_0, ми можемо скомпонувати Lb01L_{b_0}^{-1} з поточним перетворенням і продовжувати додавати Lak+1L_{a_{k+1}}, Lak+2L_{a_{k+2}} і так далі, шукаючи нові заокруглення вниз, які збігатимуться, з яких ми зможемо вивести b1b_1 і так далі, доки не відновимо всі значення [b0;b1,,bm][b_0; b_1, \dots, b_m].

Арифметика ланцюгових дробів

Нехай A=[a0;a1,,an]A=[a_0; a_1, \dots, a_n] і B=[b0;b1,,bm]B=[b_0; b_1, \dots, b_m]. Обчисліть представлення ланцюговими дробами чисел A+BA+B і ABA \cdot B.

Розв'язок

Ідея тут схожа на попередню задачу, але замість L(x)=ax+bcx+dL(x) = \frac{ax+b}{cx+d} слід розглянути білінійне дробове перетворення L(x,y)=axy+bx+cy+dexy+fx+gy+hL(x, y) = \frac{axy+bx+cy+d}{exy+fx+gy+h}.

Замість L(x)L(Lak(x))L(x) \mapsto L(L_{a_k}(x)) ви змінюватимете поточне перетворення як L(x,y)L(Lak(x),y)L(x, y) \mapsto L(L_{a_k}(x), y) або L(x,y)L(x,Lbk(y))L(x, y) \mapsto L(x, L_{b_k}(y)).

Потім ви перевіряєте, чи ae=bf=cg=dh\lfloor \frac{a}{e} \rfloor = \lfloor \frac{b}{f} \rfloor = \lfloor \frac{c}{g} \rfloor = \lfloor \frac{d}{h} \rfloor, і якщо вони всі збігаються, ви використовуєте це значення як ckc_k у результуючому дробі та змінюєте перетворення як

L(x,y)1L(x,y)ck.L(x, y) \mapsto \frac{1}{L(x, y) - c_k}.
Означення

Ланцюговий дріб x=[a0;a1,]x = [a_0; a_1, \dots] називається періодичним, якщо x=[a0;a1,,ak,x]x = [a_0; a_1, \dots, a_k, x] для деякого kk.

Ланцюговий дріб x=[a0;a1,]x = [a_0; a_1, \dots] називається зрештою періодичним, якщо x=[a0;a1,,ak,y]x = [a_0; a_1, \dots, a_k, y], де yy періодичний.

Для x=[1;1,1,]x = [1; 1, 1, \dots] виконується x=1+1xx = 1 + \frac{1}{x}, тож x2=x+1x^2 = x + 1. Існує загальний зв'язок між періодичними ланцюговими дробами та квадратними рівняннями. Розглянемо таке рівняння:

x=[a0;a1,,ak,x].x = [a_0; a_1, \dots, a_k, x].

З одного боку, це рівняння означає, що представлення xx ланцюговим дробом періодичне з періодом k+1k+1.

З іншого боку, використовуючи формулу для підхідних дробів, це рівняння означає, що

x=pkx+pk1qkx+qk1.x = \frac{p_k x + p_{k-1}}{q_k x + q_{k-1}}.

Тобто xx є дробово-лінійним перетворенням самого себе. З рівняння випливає, що xx є коренем рівняння другого степеня:

qkx2+(qk1pk)xpk1=0.q_k x^2 + (q_{k-1}-p_k)x - p_{k-1} = 0.

Подібне міркування виконується для ланцюгових дробів, що зрештою періодичні, тобто x=[a0;a1,,ak,y]x = [a_0; a_1, \dots, a_k, y] для y=[b0;b1,,bk,y]y=[b_0; b_1, \dots, b_k, y]. Справді, з першого рівняння виводимо, що x=L0(y)x = L_0(y), а з другого рівняння — що y=L1(y)y = L_1(y), де L0L_0 і L1L_1 — дробово-лінійні перетворення. Тому,

x=(L0L1)(y)=(L0L1L01)(x).x = (L_0 \circ L_1)(y) = (L_0 \circ L_1 \circ L_0^{-1})(x).

Можна далі довести (і це вперше зробив Лагранж), що для довільного квадратного рівняння ax2+bx+c=0ax^2+bx+c=0 з цілими коефіцієнтами його розв'язок xx є зрештою періодичним ланцюговим дробом.

Квадратична ірраціональність

Знайдіть ланцюговий дріб числа α=x+ynz\alpha = \frac{x+y\sqrt{n}}{z}, де x,y,z,nZx, y, z, n \in \mathbb Z і n>0n > 0 не є точним квадратом.

Розв'язок

Для kk-ї повної частки sks_k числа загалом виконується

α=[a0;a1,,ak1,sk]=skpk1+pk2skqk1+qk2.\alpha = [a_0; a_1, \dots, a_{k-1}, s_k] = \frac{s_k p_{k-1} + p_{k-2}}{s_k q_{k-1} + q_{k-2}}.

Тому,

sk=αqk1pk1αqkpk=qk1yn+(xqk1zpk1)qkyn+(xqkzpk).s_k = -\frac{\alpha q_{k-1} - p_{k-1}}{\alpha q_k - p_k} = -\frac{q_{k-1} y \sqrt n + (x q_{k-1} - z p_{k-1})}{q_k y \sqrt n + (xq_k-zp_k)}.

Помноживши чисельник і знаменник на (xqkzpk)qkyn(xq_k - zp_k) - q_k y \sqrt n, ми позбудемося n\sqrt n у знаменнику, тож повні частки матимуть вигляд

sk=xk+yknzk.s_k = \frac{x_k + y_k \sqrt n}{z_k}.

Знайдемо sk+1s_{k+1}, припускаючи, що sks_k відоме.

Передусім, ak=sk=xk+yknzka_k = \lfloor s_k \rfloor = \left\lfloor \frac{x_k + y_k \lfloor \sqrt n \rfloor}{z_k} \right\rfloor. Тоді,

sk+1=1skak=zk(xkzkak)+ykn=zk(xkykak)ykzkn(xkykak)2yk2n.s_{k+1} = \frac{1}{s_k-a_k} = \frac{z_k}{(x_k - z_k a_k) + y_k \sqrt n} = \frac{z_k (x_k - y_k a_k) - y_k z_k \sqrt n}{(x_k - y_k a_k)^2 - y_k^2 n}.

Отже, якщо позначити tk=xkykakt_k = x_k - y_k a_k, то виконуватиметься

\begin{align}x_{k+1} &=& z_k t_k, \ y_{k+1} &=& -y_k z_k, \ z_{k+1} &=& t_k^2 - y_k^2 n.\end{align}

Приємне в такому представленні те, що якщо ми скоротимо xk+1,yk+1,zk+1x_{k+1}, y_{k+1}, z_{k+1} на їхній найбільший спільний дільник, результат буде унікальним. Тому ми можемо використати його, щоб перевірити, чи поточний стан уже повторювався, а також щоб дізнатися, на якому попередньому індексі був цей стан.

Нижче наведено код для обчислення представлення ланцюговим дробом числа α=n\alpha = \sqrt n:

# обчислюємо ланцюговий дріб числа sqrt(n)
def sqrt(n):
n0 = math.floor(math.sqrt(n))
x, y, z = 1, 0, 1
a = []
def step(x, y, z):
a.append((x * n0 + y) // z)
t = y - a[-1]*z
x, y, z = -z*x, z*t, t**2 - n*x**2
g = math.gcd(x, math.gcd(y, z))
return x // g, y // g, z // g

used = dict()
for i in range(n):
used[x, y, z] = i
x, y, z = step(x, y, z)
if (x, y, z) in used:
return a

Використовуючи ту саму функцію step, але інші початкові xx, yy і zz, можна обчислити його для довільного x+ynz\frac{x+y \sqrt{n}}{z}.

інформація

Tavrida NU Akai Contest - Continued Fraction

Вам дано xx і kk, xx не є точним квадратом. Нехай x=[a0;a1,]\sqrt x = [a_0; a_1, \dots], знайдіть pkqk=[a0;a1,,ak]\frac{p_k}{q_k}=[a_0; a_1, \dots, a_k] для 0k1090 \leq k \leq 10^9.

Розв'язок

Після обчислення періоду x\sqrt x можна обчислити aka_k за допомогою бінарного піднесення до степеня дробово-лінійного перетворення, породженого представленням ланцюговим дробом. Щоб знайти результуюче перетворення, ви стискаєте період розміру TT в одне перетворення і повторюєте його k1T\lfloor \frac{k-1}{T}\rfloor разів, після чого вручну комбінуєте його з рештою перетворень.

x, k = map(int, input().split())

mod = 10**9+7

# компонуємо (A[0]*x + A[1]) / (A[2]*x + A[3]) і (B[0]*x + B[1]) / (B[2]*x + B[3])
def combine(A, B):
return [t % mod for t in [A[0]*B[0]+A[1]*B[2], A[0]*B[1]+A[1]*B[3], A[2]*B[0]+A[3]*B[2], A[2]*B[1]+A[3]*B[3]]]

A = [1, 0, 0, 1] # (x + 0) / (0*x + 1) = x

a = sqrt(x)

T = len(a) - 1 # період a

# застосовуємо ak + 1/x = (ak*x+1)/(1x+0) до (Ax + B) / (Cx + D)
for i in reversed(range(1, len(a))):
A = combine([a[i], 1, 1, 0], A)

def bpow(A, n):
return [1, 0, 0, 1] if not n else combine(A, bpow(A, n-1)) if n % 2 else bpow(combine(A, A), n // 2)


C = (0, 1, 0, 0) # = 1 / 0
while k % T:
i = k % T
C = combine([a[i], 1, 1, 0], C)
k -= 1

C = combine(bpow(A, k // T), C)
C = combine([a[0], 1, 1, 0], C)
print(str(C[1]) + '/' + str(C[3]))

Геометрична інтерпретація

Нехай rk=(qk;pk)\vec r_k = (q_k;p_k) для підхідного дробу rk=pkqkr_k = \frac{p_k}{q_k}. Тоді виконується таке рекурентне співвідношення:

rk=akrk1+rk2.\vec r_k = a_k \vec r_{k-1} + \vec r_{k-2}.

Нехай r=(1;r)\vec r = (1;r). Тоді кожен вектор (x;y)(x;y) відповідає числу, що дорівнює його кутовому коефіцієнту yx\frac{y}{x}.

З поняттям псевдоскалярного добутку (x1;y1)×(x2;y2)=x1y2x2y1(x_1;y_1) \times (x_2;y_2) = x_1 y_2 - x_2 y_1 можна показати (див. пояснення нижче), що

sk=rk2×rrk1×r=rk2×rrk1×r.s_k = -\frac{\vec r_{k-2} \times \vec r}{\vec r_{k-1} \times \vec r} = \left|\frac{\vec r_{k-2} \times \vec r}{\vec r_{k-1} \times \vec r}\right|.

Остання рівність зумовлена тим, що rk1r_{k-1} і rk2r_{k-2} лежать по різні боки від rr, тож псевдоскалярні добутки rk1\vec r_{k-1} і rk2\vec r_{k-2} з r\vec r мають різні знаки. З огляду на ak=ska_k = \lfloor s_k \rfloor, формула для rk\vec r_k тепер виглядає так:

rk=rk2+r×rk2r×rk1rk1.\vec r_k = \vec r_{k-2} + \left\lfloor \left| \frac{\vec r \times \vec r_{k-2}}{\vec r \times \vec r_{k-1}}\right|\right\rfloor \vec r_{k-1}.

Зауважимо, що rk×r=(q;p)×(1;r)=qrp\vec r_k \times r = (q;p) \times (1;r) = qr - p, тож

ak=qk1rpk1qk2rpk2.a_k = \left\lfloor \left| \frac{q_{k-1}r-p_{k-1}}{q_{k-2}r-p_{k-2}} \right| \right\rfloor.
Пояснення

Як ми вже зазначали, ak=ska_k = \lfloor s_k \rfloor, де sk=[ak;ak+1,ak+2,]s_k = [a_k; a_{k+1}, a_{k+2}, \dots]. З іншого боку, з рекурентного співвідношення для підхідних дробів виводимо, що

r=[a0;a1,,ak1,sk]=skpk1+pk2skqk1+qk2.r = [a_0; a_1, \dots, a_{k-1}, s_k] = \frac{s_k p_{k-1} + p_{k-2}}{s_k q_{k-1} + q_{k-2}}.

У векторній формі це переписується як

rskrk1+rk2,\vec r \parallel s_k \vec r_{k-1} + \vec r_{k-2},

що означає, що r\vec r і skrk1+rk2s_k \vec r_{k-1} + \vec r_{k-2} колінеарні (тобто мають однаковий кутовий коефіцієнт). Узявши псевдоскалярний добуток обох частин з r\vec r, отримуємо

0=sk(rk1×r)+(rk2×r),0 = s_k (\vec r_{k-1} \times \vec r) + (\vec r_{k-2} \times \vec r),

що дає остаточну формулу

sk=rk2×rrk1×r.s_k = -\frac{\vec r_{k-2} \times \vec r}{\vec r_{k-1} \times \vec r}.
Алгоритм витягування носа

Щоразу, коли ви додаєте rk1\vec r_{k-1} до вектора p\vec p, значення p×r\vec p \times \vec r збільшується на rk1×r\vec r_{k-1} \times \vec r.

Отже, ak=ska_k=\lfloor s_k \rfloor — це максимальна ціла кількість векторів rk1\vec r_{k-1}, які можна додати до rk2\vec r_{k-2} без зміни знаку векторного добутку з r\vec r.

Іншими словами, aka_k — це максимальна ціла кількість разів, скільки ви можете додати rk1\vec r_{k-1} до rk2\vec r_{k-2}, не перетинаючи пряму, задану r\vec r:

Підхідні дроби r=79=[0;1,3,2]r=\frac{7}{9}=[0;1,3,2]. Напівпідхідні дроби відповідають проміжним точкам між сірими стрілками.

На малюнку вище r2=(4;3)\vec r_2 = (4;3) отримується багаторазовим додаванням r1=(1;1)\vec r_1 = (1;1) до r0=(1;0)\vec r_0 = (1;0).

Коли вже неможливо далі додавати r1\vec r_1 до r0\vec r_0 без перетину прямої y=rxy=rx, ми переходимо на інший бік і багаторазово додаємо r2\vec r_2 до r1\vec r_1, щоб отримати r3=(9;7)\vec r_3 = (9;7).

Ця процедура породжує експоненційно довші вектори, що наближаються до прямої.

За цю властивість процедуру породження послідовних векторів підхідних дробів Борис Делоне назвав алгоритмом витягування носа.

Якщо ми поглянемо на трикутник, побудований на точках rk2\vec r_{k-2}, rk\vec r_{k} і 0\vec 0, ми помітимо, що його подвоєна площа дорівнює

rk2×rk=rk2×(rk2+akrk1)=akrk2×rk1=ak.|\vec r_{k-2} \times \vec r_k| = |\vec r_{k-2} \times (\vec r_{k-2} + a_k \vec r_{k-1})| = a_k |\vec r_{k-2} \times \vec r_{k-1}| = a_k.

У поєднанні з теоремою Піка це означає, що немає ґратчастих точок строго всередині трикутника, а єдиними ґратчастими точками на його межі є 0\vec 0 і rk2+trk1\vec r_{k-2} + t \cdot \vec r_{k-1} для всіх цілих tt таких, що 0tak0 \leq t \leq a_k. Якщо об'єднати це для всіх можливих kk, це означає, що немає цілих точок у просторі між многокутниками, утвореними векторами підхідних дробів з парними і непарними індексами.

Це, своєю чергою, означає, що rk\vec r_k з непарними коефіцієнтами утворюють опуклу оболонку ґратчастих точок з x0x \geq 0 над прямою y=rxy=rx, тоді як rk\vec r_k з парними коефіцієнтами утворюють опуклу оболонку ґратчастих точок з x>0x > 0 під прямою y=rxy=rx.

Означення

Ці многокутники також відомі як многокутники Клейна, названі на честь Фелікса Клейна, який першим запропонував цю геометричну інтерпретацію ланцюгових дробів.

Приклади задач

Тепер, коли найважливіші факти та поняття було введено, час заглибитися в конкретні приклади задач.

Опукла оболонка під прямою

Знайдіть опуклу оболонку ґратчастих точок (x;y)(x;y) таких, що 0xN0 \leq x \leq N і 0yrx0 \leq y \leq rx для r=[a0;a1,,ak]=pkqkr=[a_0;a_1,\dots,a_k]=\frac{p_k}{q_k}.

Розв'язок

Якби ми розглядали необмежену множину 0x0 \leq x, верхня опукла оболонка задавалася б самою прямою y=rxy=rx.

Однак з додатковим обмеженням xNx \leq N нам зрештою довелося б відхилитися від прямої, щоб підтримувати коректну опуклу оболонку.

Нехай t=Nqkt = \lfloor \frac{N}{q_k}\rfloor, тоді перші tt ґратчастих точок на оболонці після (0;0)(0;0) — це α(qk;pk)\alpha \cdot (q_k; p_k) для цілого 1αt1 \leq \alpha \leq t.

Однак (t+1)(qk;pk)(t+1)(q_k; p_k) не може бути наступною ґратчастою точкою, оскільки (t+1)qk(t+1)q_k більше за NN.

Щоб дістатися до наступних ґратчастих точок на оболонці, нам слід дістатися до точки (x;y)(x;y), яка відхиляється від y=rxy=rx на найменший відступ, зберігаючи при цьому xNx \leq N.

Опукла оболонка ґратчастих точок під y=47xy=\frac{4}{7}x для 0x190 \leq x \leq 19 складається з точок (0;0),(7;4),(14;8),(16;9),(18;10),(19;10)(0;0), (7;4), (14;8), (16;9), (18;10), (19;10).

Нехай (x;y)(x; y) — остання поточна точка опуклої оболонки. Тоді наступна точка (x;y)(x'; y') така, що xNx' \leq N і (x;y)(x;y)=(Δx;Δy)(x'; y') - (x; y) = (\Delta x; \Delta y) якомога ближча до прямої y=rxy=rx. Іншими словами, (Δx;Δy)(\Delta x; \Delta y) максимізує rΔxΔyr \Delta x - \Delta y за умов ΔxNx\Delta x \leq N - x і ΔyrΔx\Delta y \leq r \Delta x.

Такі точки лежать на опуклій оболонці ґратчастих точок під y=rxy=rx. Іншими словами, (Δx;Δy)(\Delta x; \Delta y) має бути нижнім напівпідхідним дробом rr.

Тобто (Δx;Δy)(\Delta x; \Delta y) має вигляд (qi1;pi1)+t(qi;pi)(q_{i-1}; p_{i-1}) + t \cdot (q_i; p_i) для деякого непарного числа ii і 0t<ai0 \leq t < a_i.

Щоб знайти такий ii, ми можемо перебрати всі можливі ii, починаючи з найбільшого, і використати t=Nxqi1qit = \lfloor \frac{N-x-q_{i-1}}{q_i} \rfloor для ii такого, що Nxqi10N-x-q_{i-1} \geq 0.

З (Δx;Δy)=(qi1;pi1)+t(qi;pi)(\Delta x; \Delta y) = (q_{i-1}; p_{i-1}) + t \cdot (q_i; p_i) умова ΔyrΔx\Delta y \leq r \Delta x зберігатиметься завдяки властивостям напівпідхідних дробів.

А t<ait < a_i виконуватиметься, тому що ми вже вичерпали напівпідхідні дроби, отримані з i+2i+2, тож x+qi1+aiqi=x+qi+1x + q_{i-1} + a_i q_i = x+q_{i+1} більше за NN.

Тепер, коли ми можемо додати (Δx;Δy)(\Delta x; \Delta y) до (x;y)(x;y) k=NxΔxk = \lfloor \frac{N-x}{\Delta x} \rfloor разів, перш ніж перевищимо NN, після чого ми спробуємо наступний напівпідхідний дріб.

// повертає [ah, ph, qh] такі, що точки r[i]=(ph[i], qh[i]) утворюють верхню опуклу оболонку
// ґратчастих точок на 0 <= x <= N і 0 <= y <= r * x, де r = [a0; a1, a2, ...]
// і на відрізку між r[i] та r[i+1] є ah[i]-1 цілих точок
auto hull(auto a, int N) {
auto [p, q] = convergents(a);
int t = N / q.back();
vector ah = {t};
vector ph = {0, t*p.back()};
vector qh = {0, t*q.back()};

for(int i = q.size() - 1; i >= 0; i--) {
if(i % 2) {
while(qh.back() + q[i - 1] <= N) {
t = (N - qh.back() - q[i - 1]) / q[i];
int dp = p[i - 1] + t * p[i];
int dq = q[i - 1] + t * q[i];
int k = (N - qh.back()) / dq;
ah.push_back(k);
ph.push_back(ph.back() + k * dp);
qh.push_back(qh.back() + k * dq);
}
}
}
return make_tuple(ah, ph, qh);
}
інформація

Timus - Crime and Punishment

Вам дано цілі числа AA, BB і NN. Знайдіть x0x \geq 0 і y0y \geq 0 такі, що Ax+ByNAx + By \leq N і Ax+ByAx + By якомога більше.

Розв'язок

У цій задачі виконується 1A,B,N21091 \leq A, B, N \leq 2 \cdot 10^9, тож її можна розв'язати за O(N)O(\sqrt N). Однак існує розв'язок за O(logN)O(\log N) із ланцюговими дробами.

Для нашої зручності ми обернемо напрямок xx, зробивши заміну xNAxx \mapsto \lfloor \frac{N}{A}\rfloor - x, так що тепер нам потрібно знайти точку (x;y)(x; y) таку, що 0xNA0 \leq x \leq \lfloor \frac{N}{A} \rfloor, ByAxN  mod  ABy - Ax \leq N \;\bmod\; A і ByAxBy - Ax якомога більше. Оптимальне yy для кожного xx має значення Ax+(NmodA)B\lfloor \frac{Ax + (N \bmod A)}{B} \rfloor.

Щоб розглядати це загальніше, ми напишемо функцію, що знаходить найкращу точку на 0xN0 \leq x \leq N і y=Ax+BCy = \lfloor \frac{Ax+B}{C} \rfloor.

Основна ідея розв'язку в цій задачі по суті повторює попередню задачу, але замість використання нижніх напівпідхідних дробів для відхилення від прямої ви використовуєте верхні напівпідхідні дроби, щоб наблизитися до прямої, не перетинаючи її та не порушуючи xNx \leq N. На жаль, на відміну від попередньої задачі, вам потрібно переконатися, що ви не перетинаєте пряму y=Ax+BCy=\frac{Ax+B}{C}, наближаючись до неї, тож слід це пам'ятати при обчисленні коефіцієнта напівпідхідного дробу tt.

# (x, y) таке, що y = (A*x+B) // C,
# Cy - Ax максимальне і 0 <= x <= N.
def closest(A, B, C, N):
# y <= (A*x + B)/C <=> diff(x, y) <= B
def diff(x, y):
return C*y-A*x
a = fraction(A, C)
p, q = convergents(a)
ph = [B // C]
qh = [0]
for i in range(2, len(q) - 1):
if i % 2 == 0:
while diff(qh[-1] + q[i+1], ph[-1] + p[i+1]) <= B:
t = 1 + (diff(qh[-1] + q[i-1], ph[-1] + p[i-1]) - B - 1) // abs(diff(q[i], p[i]))
dp = p[i-1] + t*p[i]
dq = q[i-1] + t*q[i]
k = (N - qh[-1]) // dq
if k == 0:
return qh[-1], ph[-1]
if diff(dq, dp) != 0:
k = min(k, (B - diff(qh[-1], ph[-1])) // diff(dq, dp))
qh.append(qh[-1] + k*dq)
ph.append(ph[-1] + k*dp)
return qh[-1], ph[-1]

def solve(A, B, N):
x, y = closest(A, N % A, B, N // A)
return N // A - x, y
інформація

June Challenge 2017 - Euler Sum

Обчисліть x=1Nex\sum\limits_{x=1}^N \lfloor ex \rfloor, де e=[2;1,2,1,1,4,1,1,6,1,,1,2n,1,]e = [2; 1, 2, 1, 1, 4, 1, 1, 6, 1, \dots, 1, 2n, 1, \dots] — число Ейлера і N104000N \leq 10^{4000}.

Розв'язок

Ця сума дорівнює кількості ґратчастих точок (x;y)(x;y) таких, що 1xN1 \leq x \leq N і 1yex1 \leq y \leq ex.

Після побудови опуклої оболонки точок під y=exy=ex цю кількість можна обчислити за допомогою теореми Піка:

// сума floor(k * x) для k з [1, N] і x = [a0; a1, a2, ...]
int sum_floor(auto a, int N) {
N++;
auto [ah, ph, qh] = hull(a, N);

// Кількість ґратчастих точок усередині вертикальної прямокутної трапеції
// на точках (0; 0) - (0; y1) - (dx; y2) - (dx; 0), що має
// a+1 цілих точок на відрізку (0; y1) - (dx; y2).
auto picks = [](int y1, int y2, int dx, int a) {
int b = y1 + y2 + a + dx;
int A = (y1 + y2) * dx;
return (A - b + 2) / 2 + b - (y2 + 1);
};

int ans = 0;
for(size_t i = 1; i < qh.size(); i++) {
ans += picks(ph[i - 1], ph[i], qh[i] - qh[i - 1], ah[i - 1]);
}
return ans - N;
}
інформація

NAIPC 2019 - It's a Mod, Mod, Mod, Mod World

За даними pp, qq і nn обчисліть i=1n[pimodq]\sum\limits_{i=1}^n [p \cdot i \bmod q].

Розв'язок

Ця задача зводиться до попередньої, якщо зауважити, що amodb=aabba \bmod b = a - \lfloor \frac{a}{b} \rfloor b. З цим фактом сума зводиться до

i=1n(pipiqq)=pn(n+1)2qi=1npiq.\sum\limits_{i=1}^n \left(p \cdot i - \left\lfloor \frac{p \cdot i}{q} \right\rfloor q\right) = \frac{pn(n+1)}{2}-q\sum\limits_{i=1}^n \left\lfloor \frac{p \cdot i}{q}\right\rfloor.

Однак сумування rx\lfloor rx \rfloor для xx від 11 до NN — це те, що ми вміємо з попередньої задачі.

void solve(int p, int q, int N) {
cout << p * N * (N + 1) / 2 - q * sum_floor(fraction(p, q), N) << "\n";
}
інформація

Library Checker - Sum of Floor of Linear

За даними NN, MM, AA і BB обчисліть i=0N1Ai+BM\sum\limits_{i=0}^{N-1} \lfloor \frac{A \cdot i + B}{M} \rfloor.

Розв'язок

Це найтехнічніше клопітна задача дотепер.

Можна використати той самий підхід і побудувати повну опуклу оболонку точок під прямою y=Ax+BMy = \frac{Ax+B}{M}.

Ми вже знаємо, як розв'язати це для B=0B = 0. Понад те, ми вже знаємо, як побудувати цю опуклу оболонку аж до найближчої до цієї прямої ґратчастої точки на відрізку [0,N1][0, N-1] (це робиться в задачі "Crime and Punishment" вище).

Тепер слід зауважити, що щойно ми досягли найближчої до прямої точки, ми можемо просто вважати, що пряма насправді проходить через найближчу точку, оскільки немає інших ґратчастих точок на [0,N1][0, N-1] між справжньою прямою і прямою, зміщеною трохи нижче, щоб проходити через найближчу точку.

Тобто, щоб побудувати повну опуклу оболонку під прямою y=Ax+BMy=\frac{Ax+B}{M} на [0,N1][0, N-1], ми можемо побудувати її аж до найближчої до прямої точки на [0,N1][0, N-1], а потім продовжити так, ніби пряма проходить через цю точку, повторно використовуючи алгоритм побудови опуклої оболонки з B=0B=0:

# оболонка ґратчастих (x, y) таких, що C*y <= A*x+B
def hull(A, B, C, N):
def diff(x, y):
return C*y-A*x
a = fraction(A, C)
p, q = convergents(a)
ah = []
ph = [B // C]
qh = [0]

def insert(dq, dp):
k = (N - qh[-1]) // dq
if diff(dq, dp) > 0:
k = min(k, (B - diff(qh[-1], ph[-1])) // diff(dq, dp))
ah.append(k)
qh.append(qh[-1] + k*dq)
ph.append(ph[-1] + k*dp)

for i in range(1, len(q) - 1):
if i % 2 == 0:
while diff(qh[-1] + q[i+1], ph[-1] + p[i+1]) <= B:
t = (B - diff(qh[-1] + q[i+1], ph[-1] + p[i+1])) // abs(diff(q[i], p[i]))
dp = p[i+1] - t*p[i]
dq = q[i+1] - t*q[i]
if dq < 0 or qh[-1] + dq > N:
break
insert(dq, dp)

insert(q[-1], p[-1])

for i in reversed(range(len(q))):
if i % 2 == 1:
while qh[-1] + q[i-1] <= N:
t = (N - qh[-1] - q[i-1]) // q[i]
dp = p[i-1] + t*p[i]
dq = q[i-1] + t*q[i]
insert(dq, dp)
return ah, ph, qh
інформація

OKC 2 - From Modular to Rational

Існує раціональне число pq\frac{p}{q} таке, що 1p,q1091 \leq p, q \leq 10^9. Ви можете запитати значення pq1p q^{-1} за модулем m109m \sim 10^9 для кількох простих чисел mm. Відновіть pq\frac{p}{q}.

Еквівалентне формулювання: Знайдіть xx, що дає мінімум Ax  mod  MAx \;\bmod\; M для 1xN1 \leq x \leq N.

Розв'язок

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

Може існувати кілька можливих розв'язків (p,q)(p, q) для pqr(modm)p \equiv qr \pmod m за даної остачі rr. Однак, якщо (p1,q1)(p_1, q_1) і (p2,q2)(p_2, q_2) обидва є розв'язками, то також виконується p1q2p2q1(modm)p_1 q_2 \equiv p_2 q_1 \pmod m. Припускаючи, що p1q1p2q2\frac{p_1}{q_1} \neq \frac{p_2}{q_2}, це означає, що p1q2p2q1|p_1 q_2 - p_2 q_1| становить принаймні mm.

В умові нам сказали, що 1p,q1091 \leq p, q \leq 10^9, тож якщо і p1,q1p_1, q_1, і p2,q2p_2, q_2 не перевищують 10910^9, то різниця не перевищує 101810^{18}. Для m>1018m > 10^{18} це означає, що розв'язок pq\frac{p}{q} з 1p,q1091 \leq p, q \leq 10^9 єдиний як раціональне число.

Отже, задача зводиться до такого: за даним rr за модулем mm знайти будь-яке qq таке, що 1q1091 \leq q \leq 10^9 і qr  mod  m109qr \;\bmod\; m \leq 10^9.

Це по суті те саме, що знайти qq, яке дає мінімально можливе qrmodmqr \bmod m для 1q1091 \leq q \leq 10^9.

Для qr=km+bqr = km + b це означає, що нам потрібно знайти пару (q,m)(q, m) таку, що 1q1091 \leq q \leq 10^9 і qrkm0qr - km \geq 0 мінімально можливе.

Оскільки mm стале, ми можемо поділити на нього і далі переформулювати це як: знайти qq таке, що 1q1091 \leq q \leq 10^9 і rmqk0\frac{r}{m} q - k \geq 0 мінімально можливе.

У термінах ланцюгових дробів це означає, що kq\frac{k}{q} є найкращим діофантовим наближенням до rm\frac{r}{m}, і достатньо перевірити лише нижні напівпідхідні дроби rm\frac{r}{m}.

# знаходимо Q, що мінімізує Q*r mod m для 1 <= k <= n < m
def mod_min(r, n, m):
a = fraction(r, m)
p, q = convergents(a)
for i in range(2, len(q)):
if i % 2 == 1 and (i + 1 == len(q) or q[i+1] > n):
t = (n - q[i-1]) // q[i]
return q[i-1] + t*q[i]

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

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