28. Algoritmo extendido de Euclides

El algoritmo extendido de Euclides no solo calcula el máximo común divisor: encuentra coeficientes que lo expresan como combinación lineal de los números originales. Con ellos se obtienen inversos modulares y soluciones de ecuaciones lineales.

28.1 Introducción

El algoritmo de Euclides calcula mcd(a, b) usando restos sucesivos. El algoritmo extendido conserva información adicional durante esas mismas divisiones: cómo escribir cada resto como una combinación de a y b.

El resultado conecta divisibilidad y álgebra. Es la herramienta estándar para encontrar inversos módulo m, resolver congruencias lineales y preparar operaciones utilizadas por algoritmos criptográficos.

28.2 Combinación lineal

Una combinación lineal de dos enteros a y b es cualquier número de la forma ax + by, donde x e y son enteros. Los coeficientes pueden ser positivos, negativos o cero.

Para a = 252 y b = 105:
252 · 1 + 105 · (-2) = 42.
252 · (-2) + 105 · 5 = 21.

42 y 21 son combinaciones lineales de 252 y 105.

El algoritmo extendido encontrará en particular una combinación lineal igual al máximo común divisor. No suele ser única: distintos pares de coeficientes pueden producir el mismo valor.

28.3 Identidad de Bézout

La identidad de Bézout afirma que, para enteros a y b no ambos nulos, existen enteros x e y tales que:

ax + by = mcd(a, b).

x e y se llaman coeficientes de Bézout.

Esta identidad es más fuerte que conocer el mcd. Proporciona una ecuación concreta que relaciona los números originales y abre la puerta a calcular inversos cuando el mcd vale 1.

28.4 Recordatorio del algoritmo de Euclides

Partimos del mismo ejemplo del tema anterior:

252 = 105 · 2 + 42.
105 = 42 · 2 + 21.
42 = 21 · 2 + 0.

Por lo tanto, mcd(252, 105) = 21.

El algoritmo extendido trabaja sobre estas igualdades, pero en vez de detenerse al encontrar 21, sustituye hacia atrás para expresar 21 en función de 252 y 105.

28.5 Sustitución hacia atrás

De la segunda división obtenemos 21 = 105 - 2·42. La primera división dice que 42 = 252 - 2·105. Sustituimos esa expresión:

21 = 105 - 2(252 - 2·105).
21 = 105 - 2·252 + 4·105.
21 = -2·252 + 5·105.

Hemos encontrado coeficientes de Bézout: x = -2 e y = 5. Una comprobación directa da -2·252 + 5·105 = -504 + 525 = 21.

28.6 Mantener coeficientes durante el algoritmo

La sustitución hacia atrás funciona, pero puede resultar incómoda en cálculos largos. El método extendido mantiene para cada resto r una expresión r = ax + by desde el comienzo.

r0 = 252 = 1·252 + 0·105.
r1 = 105 = 0·252 + 1·105.

Si rnuevo = ranterior - q·ractual,
sus coeficientes se actualizan con la misma resta.

Así, junto a cada resto se actualizan dos coeficientes. Cuando aparece el último resto no nulo, sus coeficientes resuelven la identidad de Bézout.

28.7 Tabla del algoritmo extendido

Para 252 y 105, la tabla registra que cada resto es igual a 252x + 105y.

Resto rCoeficiente x de 252Coeficiente y de 105
25210
10501
421-2
21-25
05-12

La fila del resto 21 contiene el resultado útil: 21 = -2·252 + 5·105. La fila final se muestra solo para completar el proceso; el mcd es el último resto distinto de cero.

28.8 Regla de actualización

Supongamos que ranterior = ax1 + by1 y ractual = ax2 + by2. Si el siguiente resto es rnuevo = ranterior - q·ractual, entonces:

xnuevo = x1 - q·x2.
ynuevo = y1 - q·y2.

Es la misma operación aplicada a los restos y a sus coeficientes.

La regla elimina la necesidad de reconstruir sustituciones al finalizar. Esta es la forma más práctica de implementar el algoritmo.

28.9 Implementación iterativa en JavaScript

function euclidesExtendido(a, b) {
  if (!Number.isSafeInteger(a) || !Number.isSafeInteger(b) || (a === 0 && b === 0)) {
    throw new Error("se requieren enteros seguros, no ambos cero");
  }

  const signoA = a < 0 ? -1 : 1;
  const signoB = b < 0 ? -1 : 1;
  let anteriorR = Math.abs(a), r = Math.abs(b);
  let anteriorX = signoA, x = 0;
  let anteriorY = 0, y = signoB;

  while (r !== 0) {
    const cociente = Math.floor(anteriorR / r);
    [anteriorR, r] = [r, anteriorR - cociente * r];
    [anteriorX, x] = [x, anteriorX - cociente * x];
    [anteriorY, y] = [y, anteriorY - cociente * y];
  }
  return { mcd: anteriorR, x: anteriorX, y: anteriorY };
}

console.log(euclidesExtendido(252, 105));
// { mcd: 21, x: -2, y: 5 }

La función devuelve un objeto con el mcd y los coeficientes de Bézout. La normalización de signos hace que el mcd sea siempre no negativo, incluso con entradas negativas.

28.10 Verificar el resultado

Una buena práctica es comprobar la identidad que devuelve el algoritmo. Para el ejemplo, los coeficientes deben satisfacer exactamente ax + by = mcd.

const resultado = euclidesExtendido(252, 105);
const comprobacion = 252 * resultado.x + 105 * resultado.y;

console.log(resultado.mcd); // 21
console.log(comprobacion);  // 21

Esta comprobación es especialmente útil al implementar algoritmos numéricos, donde una variable actualizada en el orden equivocado puede producir coeficientes erróneos aunque el mcd parezca correcto.

28.11 El caso coprimo

Si a y m son coprimos, la identidad de Bézout toma la forma ax + my = 1. Al mirar la igualdad módulo m, el término my deja resto 0 y queda:

ax + my = 1.
Entonces ax ≡ 1 (mod m).

Por lo tanto, x es el inverso multiplicativo de a módulo m.

Esta es la conexión fundamental: el algoritmo extendido encuentra un inverso modular sin probar todos los residuos posibles.

28.12 Ejemplo de inverso modular

Busquemos el inverso de 17 módulo 43. El algoritmo produce la identidad:

1 = (-5)·17 + 2·43.

Al reducir módulo 43:
(-5)·17 ≡ 1 (mod 43).

El inverso de 17 módulo 43 es -5, o bien 38.

Los dos representantes son equivalentes porque -5 ≡ 38 (mod 43). En programación normalmente se normaliza el resultado a un valor entre 0 y m - 1.

28.13 Implementar el inverso modular

function modulo(a, m) {
  return ((a % m) + m) % m;
}

function inversoModular(a, m) {
  if (!Number.isSafeInteger(m) || m <= 1) {
    throw new Error("el módulo debe ser un entero mayor que 1");
  }

  const { mcd, x } = euclidesExtendido(a, m);
  if (mcd !== 1) return null;
  return modulo(x, m);
}

console.log(inversoModular(17, 43)); // 38
console.log(inversoModular(2, 8));   // null

El valor null comunica que no existe inverso. No debe reemplazarse por cero: cero nunca puede ser un inverso multiplicativo módulo m mayor que 1.

28.14 División modular

Dividir entre a módulo m significa multiplicar por su inverso. Por ejemplo, para resolver 17x ≡ 9 (mod 43), multiplicamos por el inverso 38 de 17.

17x ≡ 9 (mod 43).
x ≡ 38·9 ≡ 342 ≡ 41 (mod 43).

Comprobación: 17·41 = 697 ≡ 9 (mod 43).

La operación solo es válida porque mcd(17, 43) = 1. Si el coeficiente no es coprimo con el módulo, hay que analizar el problema de otra manera.

28.15 Ecuaciones lineales modulares

Consideremos la ecuación ax ≡ b (mod m) y sea d = mcd(a, m). La ecuación tiene solución si y solo si d divide a b.

Si d ∤ b, no hay solución.
Si d | b, hay d soluciones incongruentes módulo m.

Si d = 1, existe una única solución módulo m.

Cuando d divide a b, se pueden dividir a, b y m por d. El coeficiente reducido queda coprimo con el módulo reducido, por lo que ya posee inverso.

28.16 Ejemplo con varias soluciones

Resolvamos 6x ≡ 8 (mod 14). Como mcd(6, 14) = 2 y 2 divide a 8, la ecuación tiene dos soluciones módulo 14.

Dividimos por 2: 3x ≡ 4 (mod 7).
El inverso de 3 módulo 7 es 5.
x ≡ 5·4 ≡ 6 (mod 7).

Módulo 14: x ≡ 6 y x ≡ 13.

Comprobación: 6·6 = 36 ≡ 8 y 6·13 = 78 ≡ 8 módulo 14. Las soluciones se separan por 7, que es el módulo reducido.

28.17 Aplicación: reconstrucción de proporciones

La identidad de Bézout también explica qué cantidades se pueden formar combinando bloques de tamaños a y b. Todos los múltiplos de mcd(a, b) pueden expresarse como ax + by con coeficientes enteros.

Como mcd(6, 10) = 2, toda combinación 6x + 10y es par.

Por ejemplo: 2 = 2·6 + (-1)·10.
Un número impar no puede escribirse como 6x + 10y.

En problemas físicos los coeficientes suelen requerirse no negativos, lo que añade restricciones. Pero la condición del mcd proporciona el criterio algebraico básico de posibilidad.

28.18 Aplicación: criptografía

Los sistemas criptográficos de clave pública usan operaciones modulares con números muy grandes. Encontrar inversos es una operación central al generar algunas claves y al realizar transformaciones algebraicas.

El algoritmo extendido permite hallar a-1 mod m cuando mcd(a, m) = 1.

La matemática es pública; la seguridad depende de problemas difíciles y parámetros enormes, no de ocultar la fórmula.

Los ejemplos de este tema son educativos. En software real se deben usar bibliotecas criptográficas auditadas, tipos de precisión adecuados y protocolos establecidos, nunca implementar un sistema seguro desde cero a partir de estas fórmulas.

28.19 Eficiencia y límites numéricos

El algoritmo extendido realiza la misma cantidad asintótica de pasos que el algoritmo de Euclides: O(log min(|a|, |b|)). Solo agrega unas pocas operaciones enteras para actualizar los coeficientes.

Con Number, los enteros son exactos solamente hasta Number.MAX_SAFE_INTEGER. Para valores mayores se debe reescribir el algoritmo con BigInt, usando 0n, 1n y operaciones exclusivamente entre valores de ese tipo.

28.20 Errores frecuentes

  • Confundir los coeficientes de Bézout con cocientes de las divisiones.
  • Olvidar que los coeficientes pueden ser negativos.
  • Usar un coeficiente x como inverso cuando el mcd no es 1.
  • Devolver un inverso negativo sin normalizarlo cuando se espera un residuo canónico.
  • Dividir una congruencia por un factor no coprimo sin verificar la cantidad de soluciones.
  • Usar Number para cálculos criptográficos o enteros fuera del rango seguro.

28.21 Qué debes recordar y conclusión

  • El algoritmo extendido devuelve x e y tales que ax + by = mcd(a, b).
  • Los coeficientes se actualizan en paralelo con los restos del algoritmo de Euclides.
  • Si mcd(a, m) = 1, el coeficiente de a es un inverso de a módulo m.
  • Un inverso modular permite resolver ecuaciones de la forma ax ≡ b (mod m).
  • La ecuación ax ≡ b (mod m) tiene solución exactamente cuando mcd(a, m) divide a b.
  • Para datos grandes se requieren enteros exactos, como BigInt.

El algoritmo extendido de Euclides transforma las divisiones sucesivas en información constructiva: no solo sabemos cuál es el mcd, sino cómo obtenerlo y cómo usarlo. En el próximo tema aplicaremos estas herramientas al contexto de la criptografía modular.