24. Aritmética modular

La aritmética modular trabaja con los restos de las divisiones. Permite modelar ciclos, índices circulares, calendarios, verificaciones y mecanismos de seguridad mediante un conjunto finito de valores.

24.1 Introducción

En aritmética ordinaria los números siguen creciendo sin límite. En aritmética modular, al llegar a cierto valor volvemos a empezar. Un reloj de 12 horas es el ejemplo más intuitivo: después de las 11 vienen las 0 o las 12, según la representación elegida.

Esta idea aparece constantemente en software: índices que recorren un buffer circular, turnos que se repiten, colores codificados, hashes, códigos de verificación y criptografía.

24.2 División euclídea

Para enteros a y m con m > 0, la división euclídea afirma que existen enteros únicos q y r tales que:

a = mq + r, con 0 ≤ r < m.

a: dividendo.
m: módulo o divisor positivo.
q: cociente entero.
r: resto.

Por ejemplo, 23 = 5 · 4 + 3. Al dividir 23 por 5, el resto es 3. En aritmética modular de módulo 5, el valor relevante de 23 es ese resto.

24.3 El módulo y los residuos

Al trabajar módulo m, los únicos residuos posibles son 0, 1, 2, ..., m - 1. Este conjunto se llama conjunto de residuos módulo m.

Módulo 5: {0, 1, 2, 3, 4}.

17 deja resto 2 al dividir por 5.
42 deja resto 2 al dividir por 5.
-3 también se normaliza al residuo 2 módulo 5.

Aunque hay infinitos enteros, en módulo m solo distinguimos m clases de residuos. Por eso la aritmética modular convierte procesos potencialmente infinitos en ciclos finitos.

24.4 El operador resto en programación

En JavaScript, el operador % calcula el resto de una división con el signo del dividendo. Para valores positivos coincide con el residuo habitual; para negativos puede requerir normalización.

console.log(23 % 5); // 3
console.log(42 % 5); // 2
console.log(-3 % 5); // -3

Matemáticamente, el residuo canónico módulo 5 debe estar entre 0 y 4. Por eso -3 se representa como 2 en ese sistema, aunque JavaScript devuelva -3 como resto.

24.5 Normalizar residuos negativos

Para obtener siempre un residuo entre 0 y m - 1 podemos aplicar la fórmula ((a % m) + m) % m, con m positivo.

function modulo(a, m) {
  if (!Number.isInteger(m) || m <= 0) throw new Error("m debe ser positivo");
  return ((a % m) + m) % m;
}

console.log(modulo(23, 5)); // 3
console.log(modulo(-3, 5)); // 2
console.log(modulo(-11, 5)); // 4

La normalización es esencial para índices circulares. Un índice negativo debe volver correctamente al final de una colección, no producir una posición inválida.

24.6 El reloj modular

En un reloj de 12 horas, sumar horas equivale a sumar y conservar el residuo módulo 12. Si empezamos en 10 y avanzamos 5 horas, obtenemos 15, cuyo residuo módulo 12 es 3.

10 + 5 = 15.
15 mod 12 = 3.

En un sistema de 24 horas:
23 + 4 = 27 y 27 mod 24 = 3.

El modelo funciona para cualquier ciclo fijo: días de la semana, rondas de un juego, rotación de turnos o posiciones de un arreglo circular.

24.7 Calcular una hora circular

function avanzarHora(hora, avance) {
  return modulo(hora + avance, 24);
}

console.log(avanzarHora(23, 4)); // 3
console.log(avanzarHora(2, -5)); // 21

La segunda llamada demuestra por qué normalizar es útil: retroceder cinco horas desde las 2 debe llegar a las 21 del día anterior.

24.8 Paridad como módulo 2

La paridad es un caso simple de aritmética modular. Un entero es par si su residuo módulo 2 es 0 e impar si su residuo es 1.

n es par si n mod 2 = 0.
n es impar si n mod 2 = 1.

17 mod 2 = 1, por lo tanto 17 es impar.
28 mod 2 = 0, por lo tanto 28 es par.

Muchas propiedades de paridad se simplifican al observar cómo se comportan los residuos al sumar o multiplicar.

24.9 Residuos y ciclos de potencias

Las potencias pueden parecer enormes, pero sus residuos módulo m suelen repetir un patrón. Por ejemplo, las potencias de 2 módulo 5 producen:

2¹ mod 5 = 2.
2² mod 5 = 4.
2³ mod 5 = 3.
2⁴ mod 5 = 1.
2⁵ mod 5 = 2.

El ciclo tiene longitud 4 y vuelve a empezar.

Reconocer ciclos permite calcular residuos de exponentes grandes sin construir el número completo.

24.10 Generar residuos de una potencia

function residuosDePotencia(base, exponenteMaximo, m) {
  const residuos = [];
  let valor = 1;

  for (let exponente = 1; exponente <= exponenteMaximo; exponente++) {
    valor = modulo(valor * base, m);
    residuos.push(valor);
  }

  return residuos;
}

console.log(residuosDePotencia(2, 8, 5)); // [2, 4, 3, 1, 2, 4, 3, 1]

Reducir después de cada multiplicación mantiene los valores pequeños y es equivalente a calcular la potencia completa y tomar el residuo al final.

24.11 Operaciones modulares

En módulo m podemos sumar, restar y multiplicar, y luego reducir el resultado. Por ejemplo, módulo 7:

(5 + 6) mod 7 = 11 mod 7 = 4.
(3 - 5) mod 7 = -2 mod 7 = 5.
(4 · 5) mod 7 = 20 mod 7 = 6.

Las operaciones están cerradas en el conjunto de residuos: el resultado final siempre puede representarse con uno de los m valores permitidos. El próximo tema formalizará esta idea mediante congruencias.

24.12 Índices circulares

Un buffer circular reutiliza posiciones al llegar al final. Si tiene capacidad m, el siguiente índice de i es (i + 1) mod m.

Capacidad 5.
Después del índice 0: 1, 2, 3, 4, 0, 1, ...

Siguiente índice: (i + 1) mod 5.

Esta estructura se usa en colas, almacenamiento de eventos, audio en streaming y sistemas que procesan datos de forma continua.

24.13 Recorrer un buffer circular

function posicionesCirculares(inicio, pasos, capacidad) {
  const posiciones = [];

  for (let i = 0; i < pasos; i++) {
    posiciones.push(modulo(inicio + i, capacidad));
  }

  return posiciones;
}

console.log(posicionesCirculares(3, 7, 5)); // [3, 4, 0, 1, 2, 3, 4]

24.14 Calendarios y días de la semana

Los días de la semana forman un ciclo de longitud 7. Si codificamos lunes como 0, martes como 1 y así sucesivamente, el día después de avanzar d días desde un índice actual i es (i + d) mod 7.

Viernes = 4.
Avanzar 5 días: (4 + 5) mod 7 = 2.
Índice 2: miércoles.

Este tipo de cálculo aparece en calendarios, planificadores y reglas periódicas. Los sistemas de fechas reales agregan detalles como meses, años bisiestos y zonas horarias, pero el ciclo semanal es modular.

24.15 Hashes y distribución en cubetas

Una tabla hash suele convertir una clave en un entero y usar el módulo de la capacidad para elegir una cubeta. La operación garantiza que el índice quede dentro del rango de la tabla.

Índice = hash(clave) mod capacidad.

Si capacidad = 10, los índices posibles son 0 a 9.
Dos claves pueden terminar en la misma cubeta: eso se llama colisión.

El módulo no elimina colisiones por sí solo; la estructura debe tener una estrategia para resolverlas. Elegir la capacidad y la función hash afecta la distribución.

24.16 Aritmética modular y criptografía

La criptografía moderna utiliza operaciones modulares con números muy grandes. Potencias, inversos y propiedades de números primos permiten construir mecanismos para cifrar, firmar y verificar información.

La idea básica: calcular es fácil.
Invertir ciertos cálculos sin información secreta puede ser difícil.

Los próximos temas desarrollarán congruencias, algoritmo de Euclides e inversos modulares, que son herramientas centrales para estas aplicaciones.

En sistemas reales no se deben inventar algoritmos criptográficos ni reemplazar bibliotecas auditadas con ejemplos didácticos.

24.17 Relación con funciones piso

Para a entero y m positivo, el resto puede expresarse usando piso:

a mod m = a - m⌊a/m⌋.

23 mod 5 = 23 - 5⌊23/5⌋
= 23 - 5·4 = 3.

La fórmula muestra que el módulo conserva la parte que no puede agruparse en bloques completos de tamaño m.

24.18 Errores frecuentes

  • Usar % con negativos sin normalizar cuando se necesita un índice no negativo.
  • Confundir el módulo con el divisor o con el cociente.
  • Olvidar que el módulo debe ser positivo en la definición estándar.
  • Usar una división real cuando el problema requiere división entera y resto.
  • Suponer que una tabla hash no tendrá colisiones por usar módulo.
  • Aplicar ejemplos criptográficos simplificados como si fueran sistemas seguros.

24.19 Qué debes recordar y conclusión

  • La aritmética modular conserva el resto de dividir por un módulo positivo.
  • Módulo m tiene m residuos canónicos: 0 a m - 1.
  • Los ciclos, relojes, calendarios e índices circulares son modelos modulares.
  • Para negativos en JavaScript conviene normalizar con ((a % m) + m) % m.
  • Los residuos de potencias pueden formar ciclos y permitir cálculos eficientes.
  • El módulo es base de hashes, validaciones y criptografía.

La aritmética modular convierte operaciones sobre enteros en cálculos dentro de un conjunto finito de residuos. En el próximo tema formalizaremos cuándo dos números representan el mismo residuo mediante congruencias.