← Euler Lab
#014

Secuencia Collatz más Larga

Cargando Python...

Longest Collatz Sequence

La secuencia de Collatz sigue estas reglas: si n es par, divide entre 2; si n es impar, multiplica por 3 y suma 1.

¿Qué número inicial menor que un millón produce la cadena más larga?

Memoización Subproblemas solapados Caché

1 La Conjetura de Collatz

También llamada "problema 3n+1", es uno de los problemas abiertos más famosos de las matemáticas. Nadie ha demostrado que siempre llegue a 1, pero tampoco se ha encontrado un contraejemplo.

n → n / 2
Si n es par
n → 3n + 1
Si n es impar

Ejemplo: Secuencia comenzando en 13

Impar (×3+1) Par (÷2) Fin

¿Por qué es un problema abierto?

La secuencia es caótica. Números cercanos pueden tener comportamientos muy diferentes:

26 → 11 pasos
27 → 111 pasos (¡10x más!)

Erdős ofreció $500 por una demostración. Dijo: "Las matemáticas aún no están listas para este tipo de problemas."

El Problema: Trabajo Repetido

Si calculamos las secuencias para 13, 26, 40, 80... ¡repetimos mucho trabajo!

13 26 80 40 13 40 20 40 20 ← Trabajo repetido

El Insight: Memoización

Si ya calculamos que collatz(40) = 9 pasos, ¿por qué calcularlo de nuevo?

Memoización = guardar resultados para no recalcular. Es la base de la programación dinámica.

Número inicial
837,799
Longitud de cadena
525 pasos

2 Fuerza Bruta (Sin Caché)

La solución directa: para cada número del 1 al 999,999, calcular la longitud de su secuencia completa. Problema: repetimos muchísimos cálculos.

Python — Fuerza bruta (lento)
// Output aparecerá aquí

¿Por qué es lento?

Cuando calculamos collatz_length(26), pasamos por 40, 20, 10, 5, 16, 8, 4, 2, 1.

Pero cuando calculamos collatz_length(40), ¡repetimos exactamente lo mismo desde 40!

# Trabajo repetido:

collatz(26): 26 → 13 → 40 → 20 → 10 → 5 → 16 → 8 → 4 → 2 → 1

collatz(40): 40 → 20 → 10 → 5 → 16 → 8 → 4 → 2 → 1

collatz(80): 80 → 40 → 20 → 10 → 5 → 16 → 8 → 4 → 2 → 1

Análisis de complejidad

Tiempo
O(n × L)

n números × L pasos promedio por secuencia

Espacio
O(1)

Solo variables locales

L puede ser muy grande (hasta ~500 para n < 1M) y muchas secuencias comparten sufijos.

3 Memoización: Recordar para no Repetir

La idea es simple: cuando calculemos collatz(40) = 9, lo guardamos. La próxima vez que lleguemos a 40, devolvemos 9 directamente.

¿Qué es la Memoización?

Memoización viene de "memo" (memorando). Es una técnica de optimización que:

  1. Guarda el resultado de funciones costosas
  2. Devuelve el resultado guardado cuando se llama con los mismos argumentos

Es útil cuando una función:

  • Es pura (mismo input → mismo output)
  • Se llama muchas veces con los mismos argumentos
  • Es costosa de calcular

Así funciona el caché:

1
→ 1
2
→ 2
4
→ 3
8
→ 4
16
→ 5
5
→ 6
10
→ 7
20
→ 8
40
→ 9
13
→ 10

Cache hits: cuando calculamos 26, llegamos a 40 y ya sabemos que son 9 pasos más.

Python — Con diccionario (caché manual)
// Output aparecerá aquí
Python — Con @cache (forma pythónica)
// Output aparecerá aquí

Comparación de rendimiento

Método Tiempo (1M números) Memoria
Sin caché ~30 segundos
O(1)
Con memoización ~0.5 segundos
O(n) caché

La memoización es un trade-off: usamos más memoria para ahorrar tiempo. En este caso, ~2MB de caché nos ahorra ~30 segundos.

¿Cuándo usar memoización?

✓ Usar cuando:

  • La función es pura (sin efectos secundarios)
  • Se llama con los mismos argumentos múltiples veces
  • El cálculo es costoso
  • Tienes memoria suficiente para el caché

✗ No usar cuando:

  • Cada llamada tiene argumentos únicos
  • El cálculo es trivial (O(1))
  • La memoria es limitada

@cache vs @lru_cache

@cache

Guarda todo para siempre. Ideal cuando la memoria no es problema.

from functools import cache

@lru_cache(maxsize=N)

Guarda los últimos N resultados (Least Recently Used). Ideal cuando la memoria es limitada.

from functools import lru_cache
Memoización @cache decorator Trade-off tiempo/memoria Subproblemas solapados