Algoritmo de Euclides extendido
Mientras que el algoritmo de Euclides calcula únicamente el máximo común divisor (GCD) de dos enteros no negativos y , la versión extendida también encuentra una forma de representar el GCD en términos de y , es decir, coeficientes e para los cuales:
Es importante notar que, por la identidad de Bézout , siempre podemos encontrar tal representación. Por ejemplo, , por lo tanto podemos representar como una combinación lineal con los términos y :
Una forma más general de ese problema se discute en el artículo sobre ecuaciones diofánticas lineales. Ese artículo se apoyará en este algoritmo.
Algoritmo
Denotaremos el GCD de y con en esta sección.
Los cambios respecto del algoritmo original son muy simples. Si recordamos el algoritmo, vemos que termina con y . Para estos parámetros podemos encontrar coeficientes fácilmente, a saber .
Partiendo de estos coeficientes , podemos ir hacia atrás por las llamadas recursivas. Todo lo que necesitamos es determinar cómo cambian los coeficientes e durante la transición de a .
Supongamos que encontramos los coeficientes para :
y queremos encontrar el par para :
Podemos representar como:
Sustituir esta expresión en la ecuación de coeficientes de da:
y, después de reordenar los términos:
Encontramos los valores de e :
Implementación
int gcd(int a, int b, int& x, int& y) {
if (b == 0) {
x = 1;
y = 0;
return a;
}
int x1, y1;
int d = gcd(b, a % b, x1, y1);
x = y1;
y = x1 - y1 * (a / b);
return d;
}La función recursiva de arriba devuelve el GCD y los valores de los coeficientes en x e y (que se pasan a la función por referencia).
Esta implementación del algoritmo de Euclides extendido produce resultados correctos también para enteros negativos.
Versión iterativa
También es posible escribir el algoritmo de Euclides extendido de forma iterativa. Como evita la recursión, el código se ejecutará un poco más rápido que la versión recursiva.
int gcd(int a, int b, int& x, int& y) {
x = 1, y = 0;
int x1 = 0, y1 = 1, a1 = a, b1 = b;
while (b1) {
int q = a1 / b1;
tie(x, x1) = make_tuple(x1, x - q * x1);
tie(y, y1) = make_tuple(y1, y - q * y1);
tie(a1, b1) = make_tuple(b1, a1 - q * b1);
}
return a1;
}Si observamos con atención las variables a1 y b1, podemos notar que toman exactamente los mismos valores que en la versión iterativa del algoritmo de Euclides normal. Así que el algoritmo al menos calculará el GCD correcto.
Para ver por qué el algoritmo calcula los coeficientes correctos, consideremos que los siguientes invariantes se cumplen en todo momento (antes de que comience el bucle while y al final de cada iteración):
Denotemos los valores al final de una iteración con una prima (), y supongamos . Del algoritmo de Euclides, tenemos:
Para que se cumpla el primer invariante, debe valer lo siguiente:
De forma análoga, para el segundo invariante debe valer:
Comparando los coeficientes de y se pueden derivar las ecuaciones de actualización de cada variable, lo que garantiza que los invariantes se mantienen a lo largo del algoritmo.
Al final sabemos que contiene el GCD, así que . Eso significa que encontramos los coeficientes pedidos.
Incluso se puede optimizar más el código y quitar las variables y , y reutilizar simplemente y . Sin embargo, si se hace eso, se pierde la capacidad de razonar sobre los invariantes.