Skip to Content

Fracciones continuas

Una fracción continua (continued fraction) es una representación de un número real como una secuencia convergente específica de números racionales. Son útiles en programación competitiva porque son fáciles de computar y se pueden usar de forma eficiente para encontrar la mejor aproximación racional posible del número real subyacente (entre todos los números cuyo denominador no exceda un valor dado).

Además de eso, las fracciones continuas están estrechamente relacionadas con el algoritmo de Euclides, lo que las hace útiles en un montón de problemas de teoría de números.

Representación en fracción continua

Definición

Sean a0,a1,,akZa_0, a_1, \dots, a_k \in \mathbb Z y a1,a2,,ak1a_1, a_2, \dots, a_k \geq 1. Entonces la expresión

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

se llama la representación en fracción continua del número racional rr y se denota de forma breve como r=[a0;a1,a2,,ak]r=[a_0;a_1,a_2,\dots,a_k].

Ejemplo

Sea r=53r = \frac{5}{3}. Hay dos formas de representarlo como fracción continua:

r=[1;1,1,1]=1+11+11+11,r=[1;1,2]=1+11+12. r=[1;1,1,1]amp;=1+11+11+11,r=[1;1,2]amp;=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}

Se puede demostrar que cualquier número racional se puede representar como fracción continua de exactamente 22 formas:

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].

Además, la longitud kk de tal fracción continua se estima como k=O(logmin(p,q))k = O(\log \min(p, q)) para r=pqr=\frac{p}{q}.

El razonamiento detrás de esto quedará claro una vez que profundicemos en los detalles de la construcción de la fracción continua.

Definición

Sea a0,a1,a2,a_0,a_1,a_2, \dots una secuencia entera tal que a1,a2,1a_1, a_2, \dots \geq 1. Sea rk=[a0;a1,,ak]r_k = [a_0; a_1, \dots, a_k]. Entonces la expresión

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

se llama la representación en fracción continua del número irracional rr y se denota de forma breve como r=[a0;a1,a2,]r = [a_0;a_1,a_2,\dots].

Nótese que para r=[a0;a1,]r=[a_0;a_1,\dots] y un entero kk, se cumple que r+k=[a0+k;a1,]r+k = [a_0+k; a_1, \dots].

Otra observación importante es que 1r=[0;a0,a1,]\frac{1}{r}=[0;a_0, a_1, \dots] cuando a0>0a_0 > 0 y 1r=[a1;a2,]\frac{1}{r} = [a_1; a_2, \dots] cuando a0=0a_0 = 0.

Definición

En la definición de arriba, los números racionales r0,r1,r2,r_0, r_1, r_2, \dots se llaman los **convergentes** de rr.

Correspondientemente, un rk=[a0;a1,,ak]=pkqkr_k = [a_0; a_1, \dots, a_k] = \frac{p_k}{q_k} individual se llama el kk-ésimo convergente de rr.

Ejemplo

Consideremos r=[1;1,1,1,]r = [1; 1, 1, 1, \dots]. Se puede demostrar por inducción que rk=Fk+2Fk+1r_k = \frac{F_{k+2}}{F_{k+1}}, donde FkF_k es la secuencia de Fibonacci definida como F0=0F_0 = 0, F1=1F_1 = 1 y Fk=Fk1+Fk2F_{k} = F_{k-1} + F_{k-2}. Por la fórmula de Binet, se sabe que

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

donde ϕ=1+521.618\phi = \frac{1+\sqrt{5}}{2} \approx 1.618 es la razón áurea y ψ=152=1ϕ0.618\psi = \frac{1-\sqrt{5}}{2} = -\frac{1}{\phi} \approx -0.618. Así,

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}.

Nótese que en este caso específico, una forma alternativa de encontrar rr sería resolver la ecuación

r=1+1r    r2=r+1.r = 1+\frac{1}{r} \implies r^2 = r + 1.

Definición

Sea rk=[a0;a1,,ak1,ak]r_k = [a_0; a_1, \dots, a_{k-1}, a_k]. Los números [a0;a1,,ak1,t][a_0; a_1, \dots, a_{k-1}, t] para 1tak1 \leq t \leq a_k se llaman **semiconvergentes**.

Típicamente nos referiremos a (semi)convergentes que son mayores que rr como (semi)convergentes superiores y a los que son menores que rr como (semi)convergentes inferiores.

Definición

Complementario a los convergentes, definimos los **[cocientes completos](https://en.wikipedia.org/wiki/Complete_quotient)** (complete quotients) como sk=[ak;ak+1,ak+2,]s_k = [a_k; a_{k+1}, a_{k+2}, \dots].

Correspondientemente, llamaremos a un sks_k individual el kk-ésimo cociente completo de rr.

De las definiciones de arriba, se puede concluir que sk1s_k \geq 1 para k1k \geq 1.

Tratando [a0;a1,,ak][a_0; a_1, \dots, a_k] como una expresión algebraica formal y permitiendo números reales arbitrarios en lugar de aia_i, obtenemos

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

En particular, r=[s0]=s0r = [s_0] = s_0. Por otro lado, podemos expresar sks_k como

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

lo que significa que podemos computar ak=ska_k = \lfloor s_k \rfloor y sk+1=(skak)1s_{k+1} = (s_k - a_k)^{-1} a partir de sks_k.

La secuencia a0,a1,a_0, a_1, \dots está bien definida salvo que sk=aks_k=a_k, lo cual solo ocurre cuando rr es un número racional.

Así, la representación en fracción continua está definida de forma única para cualquier número irracional rr.

Implementación

En los fragmentos de código asumiremos principalmente fracciones continuas finitas.

A partir de sks_k, la transición a sk+1s_{k+1} se ve como

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

De esta expresión, el siguiente cociente completo sk+1s_{k+1} se obtiene como

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

Para sk=pqs_k=\frac{p}{q} significa que

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}.

Así, el cómputo de una representación en fracción continua para r=pqr=\frac{p}{q} sigue los pasos del algoritmo de Euclides para pp y qq.

De esto también se sigue que gcd(pk,qk)=1\gcd(p_k, q_k) = 1 para pkqk=[a0;a1,,ak]\frac{p_k}{q_k} = [a_0; a_1, \dots, a_k]. Por tanto, los convergentes son siempre irreducibles.

=== “C++” cpp 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; } === “Python” py def fraction(p, q): a = [] while q: a.append(p // q) p, q = q, p % q return a

Resultados clave

Para dar alguna motivación para el estudio posterior de las fracciones continuas, damos ahora algunos hechos clave.

Recurrencia

Para los convergentes rk=pkqkr_k = \frac{p_k}{q_k}, se cumple la siguiente recurrencia, que permite su cómputo rápido:

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}},

donde p1q1=10\frac{p_{-1}}{q_{-1}}=\frac{1}{0} y p2q2=01\frac{p_{-2}}{q_{-2}}=\frac{0}{1}.

Desviaciones

La desviación de rk=pkqkr_k = \frac{p_k}{q_k} respecto de rr se puede estimar en general como

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

Multiplicando ambos lados por qkq_k, obtenemos una estimación alternativa:

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

De la recurrencia de arriba se sigue que qkq_k crece al menos tan rápido como los números de Fibonacci.

En la imagen de abajo se puede ver la visualización de cómo los convergentes rkr_k se aproximan a r=1+52r=\frac{1+\sqrt 5}{2}:

r=1+52r=\frac{1+\sqrt 5}{2} está representado por la línea punteada azul. Los convergentes impares se aproximan desde arriba y los convergentes pares se aproximan desde abajo.

Envolventes de retículo

Consideremos las envolventes convexas de los puntos por encima y por debajo de la recta y=rxy=rx.

Los convergentes impares (qk;pk)(q_k;p_k) son los vértices de la envolvente superior, mientras que los convergentes pares (qk;pk)(q_k;p_k) son los vértices de la envolvente inferior.

Todos los vértices enteros de las envolventes se obtienen como (q;p)(q;p) tales que

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

para entero 0tak0 \leq t \leq a_k. En otras palabras, el conjunto de puntos de retículo en las envolventes corresponde al conjunto de semiconvergentes.

En la imagen de abajo, se pueden ver los convergentes y semiconvergentes (puntos grises intermedios) de r=97r=\frac{9}{7}.

Mejores aproximaciones

Sea pq\frac{p}{q} la fracción que minimiza rpq\left|r-\frac{p}{q}\right| sujeta a qxq \leq x para algún xx.

Entonces pq\frac{p}{q} es un semiconvergente de rr.

El último hecho permite encontrar las mejores aproximaciones racionales de rr comprobando sus semiconvergentes.

Abajo se encontrará la explicación adicional y un poco de intuición e interpretación de estos hechos.

Convergentes

Miremos más de cerca los convergentes que se definieron antes. Para r=[a0,a1,a2,]r=[a_0, a_1, a_2, \dots], sus convergentes son

r0=[a0],r1=[a0,a1],,rk=[a0,a1,,ak].\begin{gather} r_0=[a_0],\r_1=[a_0, a_1],\ \dots,\ r_k=[a_0, a_1, \dots, a_k]. \end{gather}

Los convergentes son el concepto central de las fracciones continuas, así que es importante estudiar sus propiedades.

Para el número rr, su kk-ésimo convergente rk=pkqkr_k = \frac{p_k}{q_k} se puede computar como

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}},

donde Pk(a0,,ak)P_k(a_0,\dots,a_k) es el continuante , un polinomio multivariado definido como

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

Así, rkr_k es una medianta  ponderada de rk1r_{k-1} y rk2r_{k-2}.

Por consistencia, se definen dos convergentes adicionales r1=10r_{-1} = \frac{1}{0} y r2=01r_{-2} = \frac{0}{1}.

Explicación detallada

El numerador y el denominador de rkr_k se pueden ver como polinomios multivariados de 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)}.

De la definición de convergentes,

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)}.

De esto se sigue Qk(a0,,ak)=Pk1(a1,,ak)Q_k(a_0, \dots, a_k) = P_{k-1}(a_1, \dots, a_k). Esto da la relación

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).

Inicialmente, r0=a01r_0 = \frac{a_0}{1} y r1=a0a1+1a1r_1 = \frac{a_0 a_1 + 1}{a_1}, así

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

Por consistencia, es conveniente definir P1=1P_{-1} = 1 y P2=0P_{-2}=0 y decir formalmente que r1=10r_{-1} = \frac{1}{0} y r2=01r_{-2}=\frac{0}{1}.

Del análisis numérico, se sabe que el determinante de una matriz tridiagonal arbitraria

Tk=det[a0b000c0a1b100c1a2..ck100bk1ak]T_k = \det [a0amp;b0amp;0amp;amp;0c0amp;a1amp;b1amp;amp;00amp;c1amp;a2amp;.amp;amp;amp;.amp;amp;ck10amp;0amp;amp;bk1amp;ak]\begin{bmatrix} a_0 &amp; b_0 &amp; 0 &amp; \dots &amp; 0 \ c_0 &amp; a_1 &amp; b_1 &amp; \dots &amp; 0 \ 0 &amp; c_1 &amp; a_2 &amp; . &amp; \vdots \ \vdots &amp; \vdots &amp; . &amp; \ddots &amp; c_{k-1} \ 0 &amp; 0 &amp; \dots &amp; b_{k-1} &amp; a_k \end{bmatrix}

se puede computar de forma recursiva como Tk=akTk1bk1ck1Tk2T_k = a_k T_{k-1} - b_{k-1} c_{k-1} T_{k-2}. Comparándolo con PkP_k, obtenemos una expresión directa

Pk=det[xk1001xk11001x2..1001x0].P_k = \det [xkamp;1amp;0amp;amp;01amp;xk1amp;1amp;amp;00amp;1amp;x2amp;.amp;amp;amp;.amp;amp;10amp;0amp;amp;1amp;x0]\begin{bmatrix} x_k &amp; 1 &amp; 0 &amp; \dots &amp; 0 \ -1 &amp; x_{k-1} &amp; 1 &amp; \dots &amp; 0 \ 0 &amp; -1 &amp; x_2 &amp; . &amp; \vdots \ \vdots &amp; \vdots &amp; . &amp; \ddots &amp; 1 \ 0 &amp; 0 &amp; \dots &amp; -1 &amp; x_0 \end{bmatrix}_{\textstyle .}

Este polinomio también se conoce como el continuante  por su estrecha relación con las fracciones continuas. El continuante no cambiará si se invierte la secuencia de la diagonal principal. Esto da una fórmula alternativa para computarlo:

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}).

Implementación

Computaremos los convergentes como un par de secuencias p2,p1,p0,p1,,pkp_{-2}, p_{-1}, p_0, p_1, \dots, p_k y q2,q1,q0,q1,,qkq_{-2}, q_{-1}, q_0, q_1, \dots, q_k:

=== “C++” cpp 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); } === “Python” py def convergents(a): p = [0, 1] q = [1, 0] for it in a: p.append(p[-1]*it + p[-2]) q.append(q[-1]*it + q[-2]) return p, q

Árboles de fracciones continuas

Hay dos formas principales de unir todas las fracciones continuas posibles en estructuras de árbol útiles.

Árbol de Stern-Brocot

El árbol de Stern-Brocot es un árbol binario de búsqueda que contiene todos los números racionales positivos distintos.

El árbol se ve en general así:

The image  by Aaron Rotenberg  is licensed under CC BY-SA 3.0 

Las fracciones 01\frac{0}{1} y 10\frac{1}{0} se mantienen “virtualmente” en los lados izquierdo y derecho del árbol respectivamente.

Luego la fracción en un nodo es una medianta a+cb+d\frac{a+c}{b+d} de dos fracciones ab\frac{a}{b} y cd\frac{c}{d} por encima de él.

La recurrencia 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}} significa que la representación en fracción continua codifica el camino a pkqk\frac{p_k}{q_k} en el árbol. Para encontrar [a0;a1,,ak,1][a_0; a_1, \dots, a_{k}, 1], hay que hacer a0a_0 movimientos a la derecha, a1a_1 movimientos a la izquierda, a2a_2 movimientos a la derecha y así sucesivamente hasta aka_k.

El padre de [a0;a1,,ak,1][a_0; a_1, \dots, a_k,1] entonces es la fracción obtenida al dar un paso atrás en la última dirección usada.

En otras palabras, es [a0;a1,,ak1,1][a_0; a_1, \dots, a_k-1,1] cuando ak>1a_k > 1 y [a0;a1,,ak1,1][a_0; a_1, \dots, a_{k-1}, 1] cuando ak=1a_k = 1.

Así los hijos de [a0;a1,,ak,1][a_0; a_1, \dots, a_k, 1] son [a0;a1,,ak+1,1][a_0; a_1, \dots, a_k+1, 1] y [a0;a1,,ak,1,1][a_0; a_1, \dots, a_k, 1, 1].

Indexemos el árbol de Stern-Brocot. Al vértice raíz se le asigna índice 11. Luego para un vértice vv, el índice de su hijo izquierdo se asigna cambiando el bit líder de vv de 11 a 1010 y para el hijo derecho, se asigna cambiando el bit líder de 11 a 1111:

En esta indexación, la representación en fracción continua de un número racional especifica la codificación por longitud de racha  (run-length encoding) de su índice binario.

Para 52=[2;2]=[2;1,1]\frac{5}{2} = [2;2] = [2;1,1], su índice es 101121011_2 y su codificación por longitud de racha, considerando bits en orden ascendente, es [2;1,1][2;1,1].

Otro ejemplo es 25=[0;2,2]=[0;2,1,1]\frac{2}{5} = [0;2,2]=[0;2,1,1], que tiene índice 110021100_2 y su codificación por longitud de racha es, en efecto, [0;2,2][0;2,2].

Vale la pena notar que el árbol de Stern-Brocot es, de hecho, un treap. Es decir, es un árbol binario de búsqueda por pq\frac{p}{q}, pero es un heap tanto por pp como por qq.

Comparar fracciones continuas

Nos dan A=[a0;a1,,an]A=[a_0; a_1, \dots, a_n] y B=[b0;b1,,bm]B=[b_0; b_1, \dots, b_m]. ¿Qué fracción es menor?

Solución

Asumamos por ahora que AA y BB son irracionales y sus representaciones en fracción continua denotan un descenso infinito en el árbol de Stern-Brocot.

Como ya mencionamos, en esta representación a0a_0 denota el número de giros a la derecha en el descenso, a1a_1 denota el número de giros a la izquierda consecuentes y así sucesivamente. Por lo tanto, cuando comparamos aka_k y bkb_k, si ak=bka_k = b_k deberíamos simplemente pasar a comparar ak+1a_{k+1} y bk+1b_{k+1}. En caso contrario, si estamos en descensos a la derecha, deberíamos comprobar si ak<bka_k < b_k y si estamos en descensos a la izquierda, deberíamos comprobar si ak>bka_k > b_k para decir si A<BA < B.

En otras palabras, para AA y BB irracionales sería A<BA < B si y solo si (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) con comparación lexicográfica.

Ahora, usando formalmente \infty como elemento de la representación en fracción continua es posible emular números irracionales AεA-\varepsilon y A+εA+\varepsilon, es decir, elementos que son menores (mayores) que AA, pero mayores (menores) que cualquier otro número real. Específicamente, para A=[a0;a1,,an]A=[a_0; a_1, \dots, a_n], uno de estos dos elementos se puede emular como [a0;a1,,an,][a_0; a_1, \dots, a_n, \infty] y el otro se puede emular como [a0;a1,,an1,1,][a_0; a_1, \dots, a_n - 1, 1, \infty].

Cuál corresponde a AεA-\varepsilon y cuál a A+εA+\varepsilon se puede determinar por la paridad de nn o comparándolos como números irracionales.

=== “Python” ```py # check if a < b assuming that a[-1] = b[-1] = infty and a != b def less(a, b): a = [(-1)**ia[i] for i in range(len(a))] b = [(-1)**ib[i] for i in range(len(b))] return a < b

# [a0; a1, ..., ak] -> [a0, a1, ..., ak-1, 1] def expand(a): if a: # empty a = inf a[-1] -= 1 a.append(1) return a # return 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) ```

Mejor punto interior

Nos dan 01p0q0<p1q110\frac{0}{1} \leq \frac{p_0}{q_0} < \frac{p_1}{q_1} \leq \frac{1}{0}. Encontrar el número racional pq\frac{p}{q} tal que (q;p)(q; p) es lexicográficamente el más pequeño y p0q0<pq<p1q1\frac{p_0}{q_0} < \frac{p}{q} < \frac{p_1}{q_1}.

Solución

En términos del árbol de Stern-Brocot significa que necesitamos encontrar el LCA de p0q0\frac{p_0}{q_0} y p1q1\frac{p_1}{q_1}. Debido a la conexión entre el árbol de Stern-Brocot y las fracciones continuas, este LCA correspondería aproximadamente al mayor prefijo común de las representaciones en fracción continua de p0q0\frac{p_0}{q_0} y p1q1\frac{p_1}{q_1}.

Así, si p0q0=[a0;a1,,ak1,ak,]\frac{p_0}{q_0} = [a_0; a_1, \dots, a_{k-1}, a_k, \dots] y p1q1=[a0;a1,,ak1,bk,]\frac{p_1}{q_1} = [a_0; a_1, \dots, a_{k-1}, b_k, \dots] son números irracionales, el LCA es [a0;a1,,min(ak,bk)+1][a_0; a_1, \dots, \min(a_k, b_k)+1].

Para r0r_0 y r1r_1 racionales, uno de ellos podría ser el LCA mismo, lo que nos requeriría hacer análisis por casos. Para simplificar la solución para r0r_0 y r1r_1 racionales, es posible usar la representación en fracción continua de r0+εr_0 + \varepsilon y r1εr_1 - \varepsilon que se derivó en el problema anterior.

=== “Python” py # finds lexicographically smallest (q, p) # such that 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](https://codingcompetitions.withgoogle.com/codejam/round/0000000000051679/0000000000146184)

Nos dan NN pares de enteros positivos (Ci,Ji)(C_i, J_i). Hay que encontrar un par de enteros positivos (x,y)(x, y) tal que Cix+JiyC_i x + J_i y es una secuencia estrictamente creciente.

Entre tales pares, encontrar el lexicográficamente mínimo.

Solución

Reformulando el enunciado, Aix+BiyA_i x + B_i y debe ser positivo para todo ii, donde Ai=CiCi1A_i = C_i - C_{i-1} y Bi=JiJi1B_i = J_i - J_{i-1}.

Entre tales ecuaciones tenemos cuatro grupos significativos para Aix+Biy>0A_i x + B_i y > 0:

  1. Ai,Bi>0A_i, B_i > 0 se puede ignorar ya que buscamos x,y>0x, y > 0.
  2. Ai,Bi0A_i, B_i \leq 0 daría “IMPOSSIBLE” como respuesta.
  3. Ai>0A_i > 0, Bi0B_i \leq 0. Tales restricciones son equivalentes a yx<AiBi\frac{y}{x} < \frac{A_i}{-B_i}.
  4. Ai0A_i \leq 0, Bi>0B_i > 0. Tales restricciones son equivalentes a yx>AiBi\frac{y}{x} > \frac{-A_i}{B_i}.

Sea p0q0\frac{p_0}{q_0} el mayor AiBi\frac{-A_i}{B_i} del cuarto grupo y p1q1\frac{p_1}{q_1} el menor AiBi\frac{A_i}{-B_i} del tercer grupo.

El problema es ahora, dado p0q0<p1q1\frac{p_0}{q_0} < \frac{p_1}{q_1}, encontrar una fracción pq\frac{p}{q} tal que (q;p)(q;p) es lexicográficamente el más pequeño y p0q0<pq<p1q1\frac{p_0}{q_0} < \frac{p}{q} < \frac{p_1}{q_1}. === “Python” ```py 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 > p0B: p0, q0 = -A, B elif B < 0 and A > 0: # y/x < A/(-B) if B < 0 if Aq1 < p1(-B): p1, q1 = A, -B if p0q1 >= p1q0 or fail: return ‘IMPOSSIBLE’

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

Árbol de Calkin-Wilf

Una forma algo más simple de organizar fracciones continuas en un árbol binario es el árbol de Calkin-Wilf .

El árbol se ve en general así:

The image  by Olli Niemitalo , Proz  is licensed under CC0 1.0 

En la raíz del árbol se ubica el número 11\frac{1}{1}. Luego, para el vértice con un número pq\frac{p}{q}, sus hijos son pp+q\frac{p}{p+q} y p+qq\frac{p+q}{q}.

A diferencia del árbol de Stern-Brocot, el árbol de Calkin-Wilf no es un árbol binario de búsqueda, así que no se puede usar para realizar búsqueda binaria racional.

En el árbol de Calkin-Wilf, el padre directo de una fracción pq\frac{p}{q} es pqq\frac{p-q}{q} cuando p>qp>q y pqp\frac{p}{q-p} en caso contrario.

Para el árbol de Stern-Brocot, usamos la recurrencia de convergentes. Para trazar la conexión entre la fracción continua y el árbol de Calkin-Wilf, deberíamos recordar la recurrencia de los cocientes completos. Si sk=pqs_k = \frac{p}{q}, entonces sk+1=qpmodq=qpp/qqs_{k+1} = \frac{q}{p \mod q} = \frac{q}{p-\lfloor p/q \rfloor \cdot q}.

Por otro lado, si vamos repetidamente de sk=pqs_k = \frac{p}{q} a su padre en el árbol de Calkin-Wilf cuando p>qp > q, terminaremos en pmodqq=1sk+1\frac{p \mod q}{q} = \frac{1}{s_{k+1}}. Si continuamos haciéndolo, terminaremos en sk+2s_{k+2}, luego 1sk+3\frac{1}{s_{k+3}} y así sucesivamente. De esto podemos deducir que:

  1. Cuando a0>0a_0> 0, el padre directo de [a0;a1,,ak][a_0; a_1, \dots, a_k] en el árbol de Calkin-Wilf es pqq=[a01;a1,,ak]\frac{p-q}{q}=[a_0 - 1; a_1, \dots, a_k].
  2. Cuando a0=0a_0 = 0 y a1>1a_1 > 1, su padre directo es pqp=[0;a11,a2,,ak]\frac{p}{q-p} = [0; a_1 - 1, a_2, \dots, a_k].
  3. Y cuando a0=0a_0 = 0 y a1=1a_1 = 1, su padre directo es pqp=[a2;a3,,ak]\frac{p}{q-p} = [a_2; a_3, \dots, a_k].

Correspondientemente, los hijos de pq=[a0;a1,,ak]\frac{p}{q} = [a_0; a_1, \dots, a_k] son

  1. p+qq=1+pq\frac{p+q}{q}=1+\frac{p}{q}, que es [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}}, que es [0,1,a0,a1,,ak][0, 1, a_0, a_1, \dots, a_k] para a0>0a_0 > 0 y [0,a1+1,a2,,ak][0, a_1+1, a_2, \dots, a_k] para a0=0a_0=0.

Notablemente, si enumeramos los vértices del árbol de Calkin-Wilf en el orden de búsqueda en anchura (es decir, la raíz tiene número 11, y los hijos del vértice vv tienen índices 2v2v y 2v+12v+1 respectivamente), el índice del número racional en el árbol de Calkin-Wilf sería el mismo que en el árbol de Stern-Brocot.

Así, los números en los mismos niveles del árbol de Stern-Brocot y del árbol de Calkin-Wilf son los mismos, pero su ordenamiento difiere a través de la permutación de inversión de bits .

Convergencia

Para el número rr y su kk-ésimo convergente rk=pkqkr_k=\frac{p_k}{q_k} se cumple la siguiente fórmula:

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

En particular, significa que

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

y

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

De esto podemos concluir que

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

La última desigualdad se debe al hecho de que rkr_k y rk+1r_{k+1} generalmente se ubican en lados distintos de rr, así

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

Explicación detallada

Para estimar rrk|r-r_k|, empezamos estimando la diferencia entre convergentes adyacentes. Por definición,

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}}.

Reemplazando pkp_k y qkq_k en el numerador con sus recurrencias, obtenemos

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

así el numerador de rkrk1r_k - r_{k-1} es siempre el numerador negado de rk1rk2r_{k-1} - r_{k-2}. Este, a su vez, es igual a 11 para

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

así

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

Esto da una representación alternativa de rkr_k como suma parcial de una serie infinita:

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}}.

De la relación recurrente se sigue que qkq_k aumenta de forma monótona al menos tan rápido como los números de Fibonacci, así

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}}

siempre está bien definido, ya que la serie subyacente siempre converge. Notablemente, la serie residual

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

tiene el mismo signo que (1)k(-1)^k debido a lo rápido que disminuye qiqi1q_i q_{i-1}. Por tanto los rkr_k de índice par se aproximan a rr desde abajo mientras que los rkr_k de índice impar se aproximan desde arriba:

_Convergentes de r=ϕ=1+52=[1;1,1,]r=\phi = \frac{1+\sqrt{5}}{2}=[1;1,1,\dots] y su distancia a rr._

De esta imagen podemos ver que

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

así la distancia entre rr y rkr_k nunca es mayor que la distancia entre rkr_k y 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}.

¿Euclides extendido?

Nos dan A,B,CZA, B, C \in \mathbb Z. Encontrar x,yZx, y \in \mathbb Z tales que Ax+By=CAx + By = C.

Solución

Aunque este problema se resuelve típicamente con el [algoritmo de Euclides extendido](../algebra/extended-euclid-algorithm.md), hay una solución simple y directa con fracciones continuas.

Sea AB=[a0;a1,,ak]\frac{A}{B}=[a_0; a_1, \dots, a_k]. Se demostró arriba que pkqk1pk1qk=(1)k1p_k q_{k-1} - p_{k-1} q_k = (-1)^{k-1}. Sustituyendo pkp_k y qkq_k por AA y BB, obtenemos

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

donde g=gcd(A,B)g = \gcd(A, B). Si CC es divisible por gg, entonces la solución es x=(1)k1Cgqk1x = (-1)^{k-1}\frac{C}{g} q_{k-1} e y=(1)kCgpk1y = (-1)^{k}\frac{C}{g} p_{k-1}.

=== “Python” py # return (x, y) such that Ax+By=C # assumes that such (x, y) exists def dio(A, B, C): p, q = convergents(fraction(A, B)) C //= A // p[-1] # divide by gcd(A, B) t = (-1) if len(p) % 2 else 1 return t*C*q[-2], -t*C*p[-2]

Transformaciones lineales fraccionarias

Otro concepto importante para las fracciones continuas son las llamadas transformaciones lineales fraccionarias  (linear fractional transformations).

Definición

Una **transformación lineal fraccionaria** es una función f:RRf : \mathbb R \to \mathbb R tal que f(x)=ax+bcx+df(x) = \frac{ax+b}{cx+d} para algunos a,b,c,dRa,b,c,d \in \mathbb R.

Una composición (L0L1)(x)=L0(L1(x))(L_0 \circ L_1)(x) = L_0(L_1(x)) de transformaciones lineales fraccionarias L0(x)=a0x+b0c0x+d0L_0(x)=\frac{a_0 x + b_0}{c_0 x + d_0} y L1(x)=a1x+b1c1x+d1L_1(x)=\frac{a_1 x + b_1}{c_1 x + d_1} es ella misma una transformación lineal fraccionaria:

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)}.

La inversa de una transformación lineal fraccionaria también es una transformación lineal fraccionaria:

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](https://dmoj.ca/problem/dmopc19c7p4)

Nos dan un arreglo de enteros positivos a1,,ana_1, \dots, a_n. Hay que responder mm consultas. Cada consulta es computar [al;al+1,,ar][a_l; a_{l+1}, \dots, a_r].

Solución

Podemos resolver este problema con el Árbol de Segmentos si somos capaces de concatenar fracciones continuas.

Es generalmente cierto que [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]].

Denotemos 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}. Nótese que Lk()=akL_k(\infty) = a_k. En esta noción, se cumple que

[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}}.

Así, el problema se reduce al cómputo de

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

La composición de transformaciones es asociativa, así que es posible computar en cada nodo de un Árbol de Segmentos la composición de transformaciones en su subárbol.

Transformación lineal fraccionaria de una fracción continua

Sea L(x)=ax+bcx+dL(x) = \frac{ax+b}{cx+d}. Computar la representación en fracción continua [b0;b1,,bm][b_0; b_1, \dots, b_m] de L(A)L(A) para A=[a0;a1,,an]A=[a_0; a_1, \dots, a_n].

Esto permite computar A+pq=qA+pqA + \frac{p}{q} = \frac{qA + p}{q} y Apq=pAqA \cdot \frac{p}{q} = \frac{p A}{q} para cualquier pq\frac{p}{q}.

Solución

Como notamos arriba, [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), de ahí 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).

De ahí, agregando consecutivamente La0L_{a_0}, La1L_{a_1} y así sucesivamente seríamos capaces de computar

(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}.

Como L(x)L(x) es invertible, también es monótona en xx. Por lo tanto, para cualquier x0x \geq 0 se cumple que L(pkx+pk1qkx+qk1)L(\frac{p_k x + p_{k-1}}{q_k x + q_{k-1}}) está entre L(pkqk)=akckL(\frac{p_k}{q_k}) = \frac{a_k}{c_k} y L(pk1qk1)=bkdkL(\frac{p_{k-1}}{q_{k-1}}) = \frac{b_k}{d_k}.

Además, para x=[ak+1;,an]x=[a_{k+1}; \dots, a_n] es igual a L(A)L(A). De ahí, b0=L(A)b_0 = \lfloor L(A) \rfloor está entre L(pkqk)\lfloor L(\frac{p_k}{q_k}) \rfloor y L(pk1qk1)\lfloor L(\frac{p_{k-1}}{q_{k-1}}) \rfloor. Cuando son iguales, también son iguales a b0b_0.

Nótese que L(A)=(Lb0Lb1Lbm)()L(A) = (L_{b_0} \circ L_{b_1} \circ \dots \circ L_{b_m})(\infty). Conociendo b0b_0, podemos componer Lb01L_{b_0}^{-1} con la transformación actual y continuar agregando Lak+1L_{a_{k+1}}, Lak+2L_{a_{k+2}} y así sucesivamente, buscando que los nuevos suelos coincidan, de lo cual podríamos deducir b1b_1 y así sucesivamente hasta recuperar todos los valores de [b0;b1,,bm][b_0; b_1, \dots, b_m].

Aritmética de fracciones continuas

Sean A=[a0;a1,,an]A=[a_0; a_1, \dots, a_n] y B=[b0;b1,,bm]B=[b_0; b_1, \dots, b_m]. Computar las representaciones en fracción continua de A+BA+B y ABA \cdot B.

Solución

La idea aquí es similar al problema anterior, pero en lugar de L(x)=ax+bcx+dL(x) = \frac{ax+b}{cx+d} se debería considerar una transformación fraccionaria bilinear L(x,y)=axy+bx+cy+dexy+fx+gy+hL(x, y) = \frac{axy+bx+cy+d}{exy+fx+gy+h}.

En lugar de L(x)L(Lak(x))L(x) \mapsto L(L_{a_k}(x)) se cambiaría la transformación actual como L(x,y)L(Lak(x),y)L(x, y) \mapsto L(L_{a_k}(x), y) o L(x,y)L(x,Lbk(y))L(x, y) \mapsto L(x, L_{b_k}(y)).

Luego, se comprueba si ae=bf=cg=dh\lfloor \frac{a}{e} \rfloor = \lfloor \frac{b}{f} \rfloor = \lfloor \frac{c}{g} \rfloor = \lfloor \frac{d}{h} \rfloor y si todos coinciden, se usa este valor como ckc_k en la fracción resultante y se cambia la transformación como

L(x,y)1L(x,y)ck.L(x, y) \mapsto \frac{1}{L(x, y) - c_k}.

Definición

Se dice que una fracción continua x=[a0;a1,]x = [a_0; a_1, \dots] es **periódica** si x=[a0;a1,,ak,x]x = [a_0; a_1, \dots, a_k, x] para algún kk.

Se dice que una fracción continua x=[a0;a1,]x = [a_0; a_1, \dots] es eventualmente periódica si x=[a0;a1,,ak,y]x = [a_0; a_1, \dots, a_k, y], donde yy es periódica.

Para x=[1;1,1,]x = [1; 1, 1, \dots] se cumple que x=1+1xx = 1 + \frac{1}{x}, así x2=x+1x^2 = x + 1. Hay una conexión genérica entre las fracciones continuas periódicas y las ecuaciones cuadráticas. Consideremos la siguiente ecuación:

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

Por un lado, esta ecuación significa que la representación en fracción continua de xx es periódica con período k+1k+1.

Por otro lado, usando la fórmula de convergentes, esta ecuación significa que

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

Es decir, xx es una transformación lineal fraccionaria de sí misma. Se sigue de la ecuación que xx es una raíz de la ecuación de segundo grado:

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

Un razonamiento similar se aplica a las fracciones continuas que son eventualmente periódicas, es decir x=[a0;a1,,ak,y]x = [a_0; a_1, \dots, a_k, y] para y=[b0;b1,,bk,y]y=[b_0; b_1, \dots, b_k, y]. En efecto, de la primera ecuación derivamos que x=L0(y)x = L_0(y) y de la segunda ecuación que y=L1(y)y = L_1(y), donde L0L_0 y L1L_1 son transformaciones lineales fraccionarias. Por lo tanto,

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

Se puede demostrar además (y lo hizo primero Lagrange) que para una ecuación cuadrática arbitraria ax2+bx+c=0ax^2+bx+c=0 con coeficientes enteros, su solución xx es una fracción continua eventualmente periódica.

Irracionalidad cuadrática

Encontrar la fracción continua de α=x+ynz\alpha = \frac{x+y\sqrt{n}}{z} donde x,y,z,nZx, y, z, n \in \mathbb Z y n>0n > 0 no es un cuadrado perfecto.

Solución

Para el kk-ésimo cociente completo sks_k del número generalmente se cumple que

α=[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}}.

Por lo tanto,

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)}.

Multiplicando el numerador y el denominador por (xqkzpk)qkyn(xq_k - zp_k) - q_k y \sqrt n, nos desharemos de n\sqrt n en el denominador, así los cocientes completos son de la forma

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

Encontremos sk+1s_{k+1}, asumiendo que sks_k es conocido.

Primero, ak=sk=xk+yknzka_k = \lfloor s_k \rfloor = \left\lfloor \frac{x_k + y_k \lfloor \sqrt n \rfloor}{z_k} \right\rfloor. Luego,

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}.

Así, si denotamos tk=xkykakt_k = x_k - y_k a_k, se cumplirá que

xk+1=zktk,yk+1=ykzk,zk+1=tk2yk2n.\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}

Lo bueno de tal representación es que si reducimos xk+1,yk+1,zk+1x_{k+1}, y_{k+1}, z_{k+1} por su máximo común divisor, el resultado sería único. Por lo tanto, podemos usarlo para comprobar si el estado actual ya se ha repetido y también para comprobar cuál fue el índice anterior que tenía este estado.

Abajo está el código para computar la representación en fracción continua de α=n\alpha = \sqrt n:

=== “Python” ```py # compute the continued fraction of 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 = -zx, zt, t**2 - nx**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 ```

Usando la misma función step pero distintos xx, yy y zz iniciales es posible computarlo para un x+ynz\frac{x+y \sqrt{n}}{z} arbitrario.

[Tavrida NU Akai Contest - Continued Fraction](https://timus.online/problem.aspx?space=1&num=1814)

Nos dan xx y kk, xx no es un cuadrado perfecto. Sea x=[a0;a1,]\sqrt x = [a_0; a_1, \dots], encontrar pkqk=[a0;a1,,ak]\frac{p_k}{q_k}=[a_0; a_1, \dots, a_k] para 0k1090 \leq k \leq 10^9.

Solución

Después de computar el período de x\sqrt x, es posible computar aka_k usando exponenciación binaria sobre la transformación lineal fraccionaria inducida por la representación en fracción continua. Para encontrar la transformación resultante, se comprime el período de tamaño TT en una sola transformación y se repite k1T\lfloor \frac{k-1}{T}\rfloor veces, después de lo cual se combina manualmente con las transformaciones restantes.

=== “Python” ```py x, k = map(int, input().split())

mod = 10**9+7 # compose (A[0]*x + A[1]) / (A[2]*x + A[3]) and (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 # period of a # apply ak + 1/x = (ak*x+1)/(1x+0) to (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])) ```

Interpretación geométrica

Sea rk=(qk;pk)\vec r_k = (q_k;p_k) para el convergente rk=pkqkr_k = \frac{p_k}{q_k}. Entonces, se cumple la siguiente recurrencia:

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

Sea r=(1;r)\vec r = (1;r). Entonces, cada vector (x;y)(x;y) corresponde al número que es igual a su coeficiente de pendiente yx\frac{y}{x}.

Con la noción de producto seudoescalar (x1;y1)×(x2;y2)=x1y2x2y1(x_1;y_1) \times (x_2;y_2) = x_1 y_2 - x_2 y_1, se puede mostrar (véase la explicación de abajo) que

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|.

La última ecuación se debe al hecho de que rk1r_{k-1} y rk2r_{k-2} yacen en lados distintos de rr, así los productos seudoescalares de rk1\vec r_{k-1} y rk2\vec r_{k-2} con r\vec r tienen signos distintos. Con ak=ska_k = \lfloor s_k \rfloor en mente, la fórmula para rk\vec r_k ahora se ve como

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}.

Nótese que rk×r=(q;p)×(1;r)=qrp\vec r_k \times r = (q;p) \times (1;r) = qr - p, así

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.

Explicación

Como ya notamos, ak=ska_k = \lfloor s_k \rfloor, donde sk=[ak;ak+1,ak+2,]s_k = [a_k; a_{k+1}, a_{k+2}, \dots]. Por otro lado, de la recurrencia de convergentes derivamos que

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}}.

En forma vectorial, se reescribe como

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

lo que significa que r\vec r y skrk1+rk2s_k \vec r_{k-1} + \vec r_{k-2} son colineales (es decir, tienen el mismo coeficiente de pendiente). Tomando el producto seudoescalar de ambas partes con r\vec r, obtenemos

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

lo que da la fórmula final

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

Algoritmo de estiramiento de nariz

Cada vez que se agrega rk1\vec r_{k-1} al vector p\vec p, el valor de p×r\vec p \times \vec r se incrementa en rk1×r\vec r_{k-1} \times \vec r.

Así, ak=ska_k=\lfloor s_k \rfloor es el máximo número entero de vectores rk1\vec r_{k-1} que se pueden agregar a rk2\vec r_{k-2} sin cambiar el signo del producto cruz con r\vec r.

En otras palabras, aka_k es el máximo número entero de veces que se puede agregar rk1\vec r_{k-1} a rk2\vec r_{k-2} sin cruzar la recta definida por r\vec r:

_Convergentes de r=79=[0;1,3,2]r=\frac{7}{9}=[0;1,3,2]. Los semiconvergentes corresponden a puntos intermedios entre las flechas grises._

En la imagen de arriba, r2=(4;3)\vec r_2 = (4;3) se obtiene agregando repetidamente r1=(1;1)\vec r_1 = (1;1) a r0=(1;0)\vec r_0 = (1;0).

Cuando no es posible agregar más r1\vec r_1 a r0\vec r_0 sin cruzar la recta y=rxy=rx, vamos al otro lado y agregamos repetidamente r2\vec r_2 a r1\vec r_1 para obtener r3=(9;7)\vec r_3 = (9;7).

Este procedimiento genera vectores exponencialmente más largos, que se aproximan a la recta.

Por esta propiedad, el procedimiento de generar vectores convergentes consecuentes fue llamado el algoritmo de estiramiento de nariz (nose stretching algorithm) por Boris Delaunay.

Si miramos el triángulo dibujado sobre los puntos rk2\vec r_{k-2}, rk\vec r_{k} y 0\vec 0 notaremos que su área duplicada es

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.

Combinado con el teorema de Pick, significa que no hay puntos de retículo estrictamente dentro del triángulo y los únicos puntos de retículo en su borde son 0\vec 0 y rk2+trk1\vec r_{k-2} + t \cdot \vec r_{k-1} para todo entero tt tal que 0tak0 \leq t \leq a_k. Cuando se une para todos los kk posibles significa que no hay puntos enteros en el espacio entre los polígonos formados por los vectores convergentes de índice par e impar.

Esto, a su vez, significa que rk\vec r_k con coeficientes impares forman una envolvente convexa de puntos de retículo con x0x \geq 0 por encima de la recta y=rxy=rx, mientras que rk\vec r_k con coeficientes pares forman una envolvente convexa de puntos de retículo con x>0x > 0 por debajo de la recta y=rxy=rx.

Definición

Estos polígonos también se conocen como polígonos de Klein, nombrados por Felix Klein quien primero sugirió esta interpretación geométrica de las fracciones continuas.

Ejemplos de problemas

Ahora que se introdujeron los hechos y conceptos más importantes, es hora de profundizar en ejemplos de problemas específicos.

Envolvente convexa bajo la recta

Encontrar la envolvente convexa de los puntos de retículo (x;y)(x;y) tales que 0xN0 \leq x \leq N y 0yrx0 \leq y \leq rx para r=[a0;a1,,ak]=pkqkr=[a_0;a_1,\dots,a_k]=\frac{p_k}{q_k}.

Solución

Si consideráramos el conjunto no acotado 0x0 \leq x, la envolvente convexa superior estaría dada por la recta y=rxy=rx misma.

Sin embargo, con la restricción adicional xNx \leq N necesitaríamos eventualmente desviarnos de la recta para mantener una envolvente convexa adecuada.

Sea t=Nqkt = \lfloor \frac{N}{q_k}\rfloor, entonces los primeros tt puntos de retículo en la envolvente después de (0;0)(0;0) son α(qk;pk)\alpha \cdot (q_k; p_k) para entero 1αt1 \leq \alpha \leq t.

Sin embargo (t+1)(qk;pk)(t+1)(q_k; p_k) no puede ser el siguiente punto de retículo ya que (t+1)qk(t+1)q_k es mayor que NN.

Para llegar a los siguientes puntos de retículo en la envolvente, deberíamos llegar al punto (x;y)(x;y) que diverge de y=rxy=rx por el menor margen, manteniendo xNx \leq N.

La envolvente convexa de puntos de retículo bajo y=47xy=\frac{4}{7}x para 0x190 \leq x \leq 19 consiste de los puntos (0;0),(7;4),(14;8),(16;9),(18;10),(19;10)(0;0), (7;4), (14;8), (16;9), (18;10), (19;10).

Sea (x;y)(x; y) el último punto actual en la envolvente convexa. Entonces el siguiente punto (x;y)(x’; y’) es tal que xNx’ \leq N y (x;y)(x;y)=(Δx;Δy)(x’; y’) - (x; y) = (\Delta x; \Delta y) está tan cerca de la recta y=rxy=rx como sea posible. En otras palabras, (Δx;Δy)(\Delta x; \Delta y) maximiza rΔxΔyr \Delta x - \Delta y sujeto a ΔxNx\Delta x \leq N - x y ΔyrΔx\Delta y \leq r \Delta x.

Puntos como ese yacen en la envolvente convexa de puntos de retículo por debajo de y=rxy=rx. En otras palabras, (Δx;Δy)(\Delta x; \Delta y) debe ser un semiconvergente inferior de rr.

Dicho esto, (Δx;Δy)(\Delta x; \Delta y) es de la forma (qi1;pi1)+t(qi;pi)(q_{i-1}; p_{i-1}) + t \cdot (q_i; p_i) para algún número impar ii y 0t<ai0 \leq t < a_i.

Para encontrar tal ii, podemos recorrer todos los ii posibles empezando por el más grande y usar t=Nxqi1qit = \lfloor \frac{N-x-q_{i-1}}{q_i} \rfloor para ii tales que Nxqi10N-x-q_{i-1} \geq 0.

Con (Δx;Δy)=(qi1;pi1)+t(qi;pi)(\Delta x; \Delta y) = (q_{i-1}; p_{i-1}) + t \cdot (q_i; p_i), la condición ΔyrΔx\Delta y \leq r \Delta x se preservaría por las propiedades de los semiconvergentes.

Y t<ait < a_i se cumpliría porque ya agotamos los semiconvergentes obtenidos de i+2i+2, de ahí x+qi1+aiqi=x+qi+1x + q_{i-1} + a_i q_i = x+q_{i+1} es mayor que NN.

Ahora que podemos agregar (Δx;Δy)(\Delta x; \Delta y) a (x;y)(x;y) por k=NxΔxk = \lfloor \frac{N-x}{\Delta x} \rfloor veces antes de exceder NN, después de lo cual intentaríamos el siguiente semiconvergente.

=== “C++” ```cpp // returns [ah, ph, qh] such that points r[i]=(ph[i], qh[i]) constitute upper convex hull // of lattice points on 0 <= x <= N and 0 <= y <= r * x, where r = [a0; a1, a2, …] // and there are ah[i]-1 integer points on the segment between r[i] and r[i+1] auto hull(auto a, int N) { auto [p, q] = convergents(a); int t = N / q.back(); vector ah = {t}; vector ph = {0, tp.back()}; vector qh = {0, tq.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); } ```

=== “Python” py # returns [ah, ph, qh] such that points r[i]=(ph[i], qh[i]) constitute upper convex hull # of lattice points on 0 <= x <= N and 0 <= y <= r * x, where r = [a0; a1, a2, ...] # and there are ah[i]-1 integer points on the segment between r[i] and r[i+1] def hull(a, N): p, q = convergents(a) t = N // q[-1] ah = [t] ph = [0, t*p[-1]] qh = [0, t*q[-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] k = (N - qh[-1]) // dq ah.append(k) ph.append(ph[-1] + k * dp) qh.append(qh[-1] + k * dq) return ah, ph, qh

[Timus - Crime and Punishment](https://timus.online/problem.aspx?space=1&num=1430)

Nos dan números enteros AA, BB y NN. Encontrar x0x \geq 0 e y0y \geq 0 tales que Ax+ByNAx + By \leq N y Ax+ByAx + By es el máximo posible.

Solución

En este problema se cumple que 1A,B,N21091 \leq A, B, N \leq 2 \cdot 10^9, así que se puede resolver en O(N)O(\sqrt N). Sin embargo, hay una solución O(logN)O(\log N) con fracciones continuas.

Por conveniencia, invertiremos la dirección de xx haciendo una sustitución xNAxx \mapsto \lfloor \frac{N}{A}\rfloor - x, de modo que ahora hay que encontrar el punto (x;y)(x; y) tal que 0xNA0 \leq x \leq \lfloor \frac{N}{A} \rfloor, ByAxN  mod  ABy - Ax \leq N ;\bmod; A y ByAxBy - Ax es el máximo posible. El yy óptimo para cada xx tiene valor Ax+(NmodA)B\lfloor \frac{Ax + (N \bmod A)}{B} \rfloor.

Para tratarlo de forma más genérica, escribiremos una función que encuentra el mejor punto en 0xN0 \leq x \leq N e y=Ax+BCy = \lfloor \frac{Ax+B}{C} \rfloor.

La idea central de la solución en este problema esencialmente repite el problema anterior, pero en lugar de usar semiconvergentes inferiores para divergir de la recta, se usan semiconvergentes superiores para acercarse a la recta sin cruzarla y sin violar xNx \leq N. Desafortunadamente, a diferencia del problema anterior, hay que asegurarse de no cruzar la recta y=Ax+BCy=\frac{Ax+B}{C} al acercarse a ella, así que se debe tener en cuenta al calcular el coeficiente tt del semiconvergente.

=== “Python” ```py # (x, y) such that y = (Ax+B) // C, # Cy - Ax is max and 0 <= x <= N. def closest(A, B, C, N): # y <= (Ax + B)/C <=> diff(x, y) <= B def diff(x, y): return Cy-Ax 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] + tp[i] dq = q[i-1] + tq[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] + kdq) ph.append(ph[-1] + kdp) 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](https://www.codechef.com/problems/ES)

Computar x=1Nex\sum\limits_{x=1}^N \lfloor ex \rfloor, donde 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] es el número de Euler y N104000N \leq 10^{4000}.

Solución

Esta suma es igual al número de puntos de retículo (x;y)(x;y) tales que 1xN1 \leq x \leq N y 1yex1 \leq y \leq ex.

Después de construir la envolvente convexa de los puntos por debajo de y=exy=ex, este número se puede computar usando el teorema de Pick:

=== “C++” ```cpp // sum floor(k * x) for k in [1, N] and x = [a0; a1, a2, …] int sum_floor(auto a, int N) { N++; auto [ah, ph, qh] = hull(a, N);

// The number of lattice points within a vertical right trapezoid // on points (0; 0) - (0; y1) - (dx; y2) - (dx; 0) that has // a+1 integer points on the segment (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; } ```

=== “Python” ```py # sum floor(k * x) for k in [1, N] and x = [a0; a1, a2, …] def sum_floor(a, N): N += 1 ah, ph, qh = hull(a, N)

# The number of lattice points within a vertical right trapezoid # on points (0; 0) - (0; y1) - (dx; y2) - (dx; 0) that has # a+1 integer points on the segment (0; y1) - (dx; y2). def picks(y1, y2, dx, a): b = y1 + y2 + a + dx A = (y1 + y2) * dx return (A - b + 2) // 2 + b - (y2 + 1) ans = 0 for i in range(1, len(qh)): 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](https://open.kattis.com/problems/itsamodmodmodmodworld)

Dados pp, qq y nn, computar i=1n[pimodq]\sum\limits_{i=1}^n [p \cdot i \bmod q].

Solución

Este problema se reduce al anterior si se nota que amodb=aabba \bmod b = a - \lfloor \frac{a}{b} \rfloor b. Con este hecho, la suma se reduce a

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.

Sin embargo, sumar rx\lfloor rx \rfloor para xx de 11 a NN es algo de lo que somos capaces del problema anterior.

=== “C++” cpp void solve(int p, int q, int N) { cout << p * N * (N + 1) / 2 - q * sum_floor(fraction(p, q), N) << "\n"; } === “Python” py def solve(p, q, N): return p * N * (N + 1) // 2 - q * sum_floor(fraction(p, q), N)

[Library Checker - Sum of Floor of Linear](https://judge.yosupo.jp/problem/sum_of_floor_of_linear)

Dados NN, MM, AA y BB, computar i=0N1Ai+BM\sum\limits_{i=0}^{N-1} \lfloor \frac{A \cdot i + B}{M} \rfloor.

Solución

Este es el problema técnicamente más engorroso hasta ahora.

Es posible usar el mismo enfoque y construir la envolvente convexa completa de puntos por debajo de la recta y=Ax+BMy = \frac{Ax+B}{M}.

Ya sabemos cómo resolverlo para B=0B = 0. Además, ya sabemos cómo construir esta envolvente convexa hasta el punto de retículo más cercano a esta recta en el segmento [0,N1][0, N-1] (esto se hace en el problema “Crime and Punishment” de arriba).

Ahora deberíamos notar que una vez que alcanzamos el punto más cercano a la recta, podemos simplemente asumir que la recta de hecho pasa por el punto más cercano, ya que no hay otros puntos de retículo en [0,N1][0, N-1] entre la recta real y la recta movida ligeramente abajo para pasar por el punto más cercano.

Dicho esto, para construir la envolvente convexa completa por debajo de la recta y=Ax+BMy=\frac{Ax+B}{M} en [0,N1][0, N-1], podemos construirla hasta el punto más cercano a la recta en [0,N1][0, N-1] y luego continuar como si la recta pasara por este punto, reutilizando el algoritmo para construir la envolvente convexa con B=0B=0:

=== “Python” ```py # hull of lattice (x, y) such that Cy <= Ax+B def hull(A, B, C, N): def diff(x, y): return Cy-Ax 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](https://codeforces.com/gym/102354/problem/I)

Hay un número racional pq\frac{p}{q} tal que 1p,q1091 \leq p, q \leq 10^9. Se puede preguntar el valor de pq1p q^{-1} módulo m109m \sim 10^9 para varios números primos mm. Recuperar pq\frac{p}{q}.

Formulación equivalente: Encontrar xx que entrega el mínimo de Ax  mod  MAx ;\bmod; M para 1xN1 \leq x \leq N.

Solución

Por el Teorema Chino del Resto, preguntar el resultado módulo varios números primos es lo mismo que preguntarlo módulo su producto. Debido a esto, sin pérdida de generalidad asumiremos que conocemos el resto módulo un número mm suficientemente grande.

Podría haber varias soluciones posibles (p,q)(p, q) de pqr(modm)p \equiv qr \pmod m para un resto dado rr. Sin embargo, si (p1,q1)(p_1, q_1) y (p2,q2)(p_2, q_2) son ambas soluciones entonces también se cumple que p1q2p2q1(modm)p_1 q_2 \equiv p_2 q_1 \pmod m. Asumiendo que p1q1p2q2\frac{p_1}{q_1} \neq \frac{p_2}{q_2} significa que p1q2p2q1|p_1 q_2 - p_2 q_1| es al menos mm.

En el enunciado se nos dijo que 1p,q1091 \leq p, q \leq 10^9, así que si tanto p1,q1p_1, q_1 como p2,q2p_2, q_2 son a lo sumo 10910^9, entonces la diferencia es a lo sumo 101810^{18}. Para m>1018m > 10^{18} significa que la solución pq\frac{p}{q} con 1p,q1091 \leq p, q \leq 10^9 es única, como número racional.

Así, el problema se reduce, dado rr módulo mm, a encontrar cualquier qq tal que 1q1091 \leq q \leq 10^9 y qr  mod  m109qr ;\bmod; m \leq 10^9.

Esto es efectivamente lo mismo que encontrar qq que entrega el qrmodmqr \bmod m mínimo posible para 1q1091 \leq q \leq 10^9.

Para qr=km+bqr = km + b significa que hay que encontrar un par (q,m)(q, m) tal que 1q1091 \leq q \leq 10^9 y qrkm0qr - km \geq 0 es el mínimo posible.

Como mm es constante, podemos dividir por él y reenunciar además: encontrar qq tal que 1q1091 \leq q \leq 10^9 y rmqk0\frac{r}{m} q - k \geq 0 es el mínimo posible.

En términos de fracciones continuas significa que kq\frac{k}{q} es la mejor aproximación diofántica a rm\frac{r}{m} y basta con comprobar solo los semiconvergentes inferiores de rm\frac{r}{m}.

=== “Python” py # find Q that minimizes Q*r mod m for 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]

Problemas de práctica