El árbol de Stern-Brocot y las sucesiones de Farey
Árbol de Stern-Brocot
El árbol de Stern-Brocot es una construcción elegante para representar el conjunto de todas las fracciones positivas. Lo descubrieron de forma independiente el matemático alemán Moritz Stern en 1858 y el relojero francés Achille Brocot en 1861. Sin embargo, algunas fuentes atribuyen el descubrimiento al matemático griego antiguo Eratóstenes.
La construcción empieza en la iteración cero con las dos fracciones
donde cabe señalar que la segunda cantidad no es estrictamente una fracción, pero se puede interpretar como una fracción irreducible que representa el infinito.
En cada iteración posterior, consideramos todas las fracciones adyacentes y e insertamos su mediante entre ellas.
Las primeras iteraciones se ven así:
Continuando este proceso hasta el infinito esto cubre todas las fracciones positivas. Además, todas las fracciones serán únicas e irreducibles. Por último, las fracciones también aparecerán en orden creciente.
Antes de demostrar estas propiedades, mostremos realmente una visualización del árbol de Stern-Brocot, más que la representación en lista. Cada fracción del árbol tiene dos hijos. Cada hijo es el mediante del ancestro más cercano a la izquierda y del ancestro más cercano a la derecha.
Demostraciones
Orden. Demostrar el orden es sencillo. Notamos que el mediante de dos fracciones siempre está entre las fracciones
dado que
Las dos desigualdades se pueden mostrar fácilmente reescribiendo las fracciones con denominadores comunes.
Como el orden es creciente en la iteración cero, se mantendrá en cada iteración posterior.
Irreducibilidad. Para demostrar esto mostraremos que para cualesquiera dos fracciones adyacentes y tenemos que
Recordemos que una ecuación diofántica con dos variables tiene solución sii es un múltiplo de . En nuestro caso esto implica que , que es lo que queremos mostrar.
Claramente en la iteración cero . Lo que queda por mostrar es que los mediantes retienen esta propiedad.
Asumamos que nuestras dos fracciones adyacentes cumplen ; después de añadir el mediante a la lista
las nuevas expresiones se vuelven
que, usando que , se puede mostrar fácilmente que son verdaderas.
De esto vemos que la propiedad siempre se mantiene y así todas las fracciones son irreducibles.
La presencia de todas las fracciones. Esta demostración está estrechamente relacionada con localizar una fracción en el árbol de Stern-Brocot. Por la propiedad de orden tenemos que el subárbol izquierdo de una fracción contiene solo fracciones menores que la fracción padre, y el subárbol derecho contiene solo fracciones mayores que la fracción padre. Esto significa que podemos buscar una fracción recorriendo el árbol desde la raíz, yendo a la izquierda si el objetivo es menor que la fracción y yendo a la derecha si el objetivo es mayor.
Elijamos una fracción objetivo positiva arbitraria . Obviamente está entre y , así que la única forma de que la fracción no esté en el árbol es si se necesita un número infinito de pasos para llegar a ella.
Si ese es el caso, en todas las iteraciones tendríamos
que (usando el hecho de que un entero ) se puede reescribir como
Ahora multipliquemos la primera desigualdad por y la segunda por y sumémoslas para obtener
Expandiendo esto y usando la propiedad mostrada antes obtenemos que
Y dado que en cada iteración al menos uno de aumentará, el proceso de búsqueda de la fracción no contendrá más de iteraciones. Esto contradice la suposición de que el camino a era infinito y por lo tanto debe formar parte del árbol.
Algoritmo de construcción del árbol
Para construir cualquier subárbol del árbol de Stern-Brocot, basta conocer el ancestro izquierdo y el derecho. En el primer nivel, los ancestros izquierdo y derecho son y respectivamente. Usando estos, calculamos el mediante y procedemos un nivel más profundo, con el mediante reemplazando al ancestro derecho en el subárbol izquierdo, y viceversa.
Este pseudocódigo intenta construir el árbol infinito entero:
void build(int a = 0, int b = 1, int c = 1, int d = 0, int level = 1) {
int x = a + c, y = b + d;
... output the current fraction x/y at the current level in the tree
build(a, b, x, y, level + 1);
build(x, y, c, d, level + 1);
}Algoritmo de búsqueda de fracciones
El algoritmo de búsqueda ya se describió en la demostración de que todas las fracciones aparecen en el árbol, pero lo repetiremos aquí. El algoritmo es un algoritmo de búsqueda binaria. Inicialmente estamos en la raíz del árbol y comparamos nuestro objetivo con la fracción actual. Si son iguales hemos terminado y detenemos el proceso. Si nuestro objetivo es menor nos movemos al hijo izquierdo; en caso contrario nos movemos al hijo derecho.
Búsqueda naive
Aquí hay una implementación que devuelve el camino a una fracción dada como una secuencia de caracteres 'L' y 'R', que significan recorrido hacia el hijo izquierdo y el derecho respectivamente. Esta secuencia de caracteres define de forma unívoca todas las fracciones positivas y se llama sistema numérico de Stern-Brocot.
string find(int p, int q) {
int pL = 0, qL = 1;
int pR = 1, qR = 0;
int pM = 1, qM = 1;
string res;
while(pM != p || qM != q) {
if(p * qM < pM * q) {
res += 'L';
tie(pR, qR) = {pM, qM};
} else {
res += 'R';
tie(pL, qL) = {pM, qM};
}
tie(pM, qM) = pair{pL + pR, qL + qR};
}
return res;
}Los números irracionales en el sistema numérico de Stern-Brocot corresponden a secuencias infinitas de caracteres. A lo largo del camino sin fin hacia el número irracional el algoritmo hallará fracciones reducidas con denominadores gradualmente crecientes que proporcionan aproximaciones cada vez mejores del número irracional. Así, tomando un prefijo de la secuencia infinita se pueden lograr aproximaciones con cualquier precisión deseada. Esta aplicación es importante en relojería, lo que explica por qué se descubrió el árbol en ese dominio.
Nótese que para una fracción , la longitud de la secuencia resultante podría ser tan grande como , por ejemplo cuando la fracción es de la forma . Esto significa que el algoritmo de arriba no debería usarse, a menos que esta sea una complejidad aceptable.
Búsqueda logarítmica
Afortunadamente, es posible mejorar el algoritmo de arriba para garantizar complejidad . Para ello debemos notar que si las fracciones frontera actuales son y , entonces al dar pasos a la derecha nos movemos a la fracción , y al dar pasos a la izquierda, nos movemos a la fracción .
Por lo tanto, en lugar de dar pasos de L o R uno por uno, podemos dar pasos en la misma dirección de una vez, después de lo cual cambiaríamos a ir en la otra dirección, y así sucesivamente. De esta manera, podemos hallar el camino a la fracción como su codificación por longitud de racha (run-length encoding).
Como las direcciones alternan de esta manera, siempre sabremos cuál tomar. Así, por conveniencia podemos representar un camino a una fracción como una secuencia de fracciones
tal que y son las fronteras del intervalo de búsqueda en el -ésimo paso, empezando con y . Entonces, después del -ésimo paso nos movemos a una fracción
donde es un número entero positivo. Si uno está familiarizado con las fracciones continuas, reconocería que la secuencia es la secuencia de las fracciones convergentes de y la secuencia representa la fracción continua de .
Esto permite hallar la codificación por longitud de racha del camino a de la manera que sigue el algoritmo para computar la representación en fracción continua de la fracción :
auto find(int p, int q) {
bool right = true;
vector<pair<int, char>> res;
while(q) {
res.emplace_back(p / q, right ? 'R' : 'L');
tie(p, q) = pair{q, p % q};
right ^= 1;
}
res.back().first--;
return res;
}Sin embargo, este enfoque solo funciona si ya conocemos y queremos hallar su lugar en el árbol de Stern-Brocot.
En la práctica, a menudo ocurre que no se conoce de antemano, pero somos capaces de comprobar para específicos si .
Sabiendo esto, podemos emular la búsqueda en el árbol de Stern-Brocot manteniendo las fronteras actuales y , y hallando cada mediante búsqueda binaria. El algoritmo entonces es un poco más técnico y potencialmente tiene una complejidad de , a menos que la formulación del problema permita hallar más rápido (por ejemplo, usando floor de alguna expresión conocida).
Sucesión de Farey
La sucesión de Farey de orden es la secuencia ordenada de fracciones entre y cuyos denominadores no superan .
Las sucesiones llevan el nombre del geólogo inglés John Farey, que en 1816 conjeturó que cualquier fracción de una sucesión de Farey es el mediante de sus vecinas. Esto lo demostró algún tiempo después Cauchy, pero de forma independiente de ambos, el matemático Haros había llegado a casi la misma conclusión en 1802.
Las sucesiones de Farey tienen muchas propiedades interesantes por sí mismas, pero la conexión con el árbol de Stern-Brocot es la más obvia. De hecho, las sucesiones de Farey se pueden obtener recortando ramas del árbol.
Del algoritmo para construir el árbol de Stern-Brocot, obtenemos un algoritmo para las sucesiones de Farey. Empezamos con la lista de fracciones . En cada iteración posterior, insertamos el mediante solo si el denominador no supera . En algún momento la lista dejará de cambiar y se habrá hallado la sucesión de Farey deseada.
Longitud de una sucesión de Farey
Una sucesión de Farey de orden contiene todos los elementos de la sucesión de Farey de orden así como todas las fracciones irreducibles con denominador , pero esto último es simplemente la función totiente . Así que la longitud de la sucesión de Farey de orden es
o equivalentemente, desenrollando la recursión obtenemos