Lumænaut_

Los 8 paradigmas de algoritmos

Técnicas de diseño de algoritmos

Arte a color ASCII: gente caminando por una calle de una ciudad 
              europea; se ve un tranvía y una cafetería; edificios de estilo 
              europeo al fondo.
Ilustración estilo ASCII: gente caminando por una calle de una ciudad europea. Se ve un tranvía y una cafetería, con edificios de estilo europeo al fondo.

Antes de escribir una sola línea de código, eliges la forma de analizar: ¿cómo va a abordar este programa el problema? Esa elección es de lo que tratan los paradigmas de algoritmos. Un paradigma no es un algoritmo concreto como QuickSort o Dijkstra, sino la mentalidad de fondo: "divide el problema en partes más chicas", "prueba todas las opciones", "recuerda lo que ya calculaste", etc. Distintos paradigmas llevan a distintos algoritmos; el mismo paradigma aparece en sorting, teoría de grafos y tu próximo proyecto personal.

Este post habla de ocho de los paradigmas principales: las técnicas de diseño que aparecen en todos lados en ciencias de la computación, desde entrevistas hasta código en producción. Cada sección tiene una demo interactiva corta que ejemplifica el paradigma en acción, no solo en teoría.

1. Fuerza bruta

En el fondo, fuerza bruta es solo prueba y error sistemático. Pasas por todas las opciones hasta dar con la correcta. No hay trucos: solo cómputo bruto. Eso lo hace fácil de implementar y a veces es la única forma de garantizar una solución. El costo es la velocidad: cuando crece la entrada, la fuerza bruta se vuelve lenta.

Piensa en una caja fuerte de 3 dígitos. No sabes la clave, así que empiezas en 000, luego 001, 002… y así continuas hasta dar con la combinación. Eso es fuerza bruta. La demo de abajo hace exactamente eso: elige un número al azar y prueba uno por uno hasta que coincida. Usa "Siguiente" para un intento o "Ejecutar hasta abrir" para ver al algoritmo recorrer todas las posibilidades.

Fuerza bruta: candado de 3 dígitos

Demo interactiva: prueba cada combinación desde 000 hasta que se abre el candado.

Prueba cada combinación en orden hasta encontrar la correcta. Sin atajos: eso es fuerza bruta.

2. Divide y vencerás

El paradigma divide y vencerás dice: toma un problema grande, divídelo en subproblemas más chicos y parecidos, resuélvelos (a menudo de forma recursiva) y luego combina las respuestas. Es una técnica de diseño, no un solo algoritmo: la misma idea aparece en merge sort, búsqueda binaria y muchos problemas clásicos.

Piensa en dividir y vencer como ordenar una casa desordenada. En lugar de atacar todo de una vez, lo divides: una habitación a la vez. Una vez que cada habitación está en orden, la casa entera estará lista. Merge sort funciona igual: toma una lista y la va partiendo a la mitad hasta que quedan elementos sueltos (que están "ordenados" por definición). Luego junta esas partes en el orden correcto.

La demo muestra ese proceso paso a paso. Usa Siguiente paso para ver cada "dividir" (partir un segmento a la mitad; izquierda y derecha con color) y cada "vencer" (juntar dos mitades ordenadas; el segmento fusionado se resalta). Ejecutar todo corre la secuencia completa; Mezclar reinicia con un nuevo arreglo.

Divide y vencerás: merge sort paso a paso

Paso a paso: cada dividir muestra la partición; cada vencer muestra el segmento fusionado.

Avanza cada dividir (partir a la mitad) y vencer (fusionar mitades ordenadas). Anterior / Siguiente paso / Ejecutar todo / Mezclar.

3. Programación dinámica

El paradigma de programación dinámica también parte el problema en subproblemas, pero con una vuelta clave: guarda los resultados. Cuando el mismo subproblema vuelve a aparecer, reusa la respuesta guardada en lugar de recalcular. Esa "memoización" (o tabulación) es el corazón del paradigma: convierte muchas ideas de tiempo exponencial en algo factible.

El ejemplo clásico es la sucesión de Fibonacci. A lo tonto, fib(5) dispara trabajo repetido para fib(4), fib(3), etc. Con PD llenas una tabla una vez: fib(0), fib(1), fib(2)… y cada valor nuevo solo necesita ver los dos anteriores. En la demo puedes avanzar o retroceder y calcular fib(n); la tabla muestra qué se guarda y se reutiliza.

Programación dinámica: Fibonacci con memo

Tabla memo con fib(0), fib(1), … para no recalcular los mismos valores.

Calcula fib(n). La tabla guarda resultados para que cada subproblema se resuelva solo una vez.

4. Greedy (voraz)

El paradigma greedy: en cada paso, toma la mejor decisión local y no vuelvas atrás. No reconsideres lo que ya decidiste. En muchos problemas esa mentalidad da la solución óptima; en otros solo una "buena". El arte está en saber cuándo el enfoque greedy es seguro.

Dar cambio con monedas es el ejemplo típico. Con monedas de 25, 10, 5 y 1 centavo, tomar en cada paso la moneda más grande que quepa (greedy) da el menor número de monedas. Para 37¢ tomas 25, luego 10, luego dos de 1. La demo te deja poner un monto y ver la selección greedy de monedas. (Con algunas denominaciones "greedy" falla; con 25, 10, 5, 1 funciona.)

Greedy: dar cambio

Monto en centavos y las monedas elegidas por la regla greedy.

En cada paso elegimos la moneda más grande que quepa. Sin retroceder: eso es greedy.

5. Backtracking

Backtracking sigue un ritmo simple: prueba, falla, deshaz, repite. Vas haciendo elecciones una por una, pero cuando una lleva a un callejón sin salida, regresas y pruebas otra. Es más listo que fuerza bruta porque cortas malas ramas pronto en lugar de seguirlas hasta el final.

Resolver un laberinto es un buen modelo mental: avanzas hasta un callejón sin salida, luego regresas al último cruce y pruebas la otra rama. Sudoku y muchos solucionadores de puzzles funcionan igual. La demo es una cuadrícula chica con inicio y meta; "Resolver (backtrack)" encuentra un camino probando movimientos y retrocediendo cuando se atora.

Backtracking: camino en laberinto

Cuadrícula con muros; el backtracking encuentra un camino del inicio a la meta.

Haz clic en Resolver para encontrar un camino. El algoritmo prueba una dirección, retrocede si se bloquea y prueba otra.

6. Búsqueda

El paradigma de búsqueda es "encontrar algo en una estructura". Una elección clásica es la búsqueda binaria: ir partiendo el espacio de búsqueda a la mitad en una colección ordenada. La búsqueda lineal revisa elemento por elemento; la binaria es mucho más rápida cuando los datos están ordenados porque en cada paso descarta la mitad de los candidatos.

Como buscar un nombre en la guía telefónica: en lugar de leer cada página en orden, abres la mitad, luego la mitad de lo que queda, y así continúas. La demo muestra una matriz ordenada de números y ejecuta búsqueda binaria: haz clic en "Poner objetivo al azar" y luego "Ejecutar búsqueda" para ver qué celdas siguen "consideradas" y cuáles se descartan (en rojo suave) mientras se reduce el rango.

7. Ordenamiento

Ordenar es poner los datos en un orden definido: de menor a mayor o de la A a la Z. Muchos algoritmos lo hacen. Bubble, inserción, merge y quicksort son algunos, cada uno con sus trade-offs. Merge sort es divide y vencerás. Quicksort usa un pivote y partición. Bubble sort es el más simple: solo compara pares adyacentes y los intercambia cuando están desordenados.

En la demo mezclas las barras y ejecutas bubble sort. Mira cómo los valores más grandes "suben" hacia el final mientras se comparan e intercambian pares adyacentes. Una pasada sin intercambios significa que la lista está ordenada. No es el algoritmo más rápido, pero la idea es clara.

Ordenamiento: bubble sort

Las barras se reordenan con bubble sort hasta quedar ascendentes.

Mezcla las barras y ejecuta bubble sort. Se comparan e intercambian pares adyacentes hasta que la lista esté ordenada.

8. Hashing

Imagina que organizas una biblioteca enorme, pero en lugar de buscar en cada estante, tienes una regla simple: tomas el título del libro, lo pasas por un cálculo rápido y te dice exactamente en qué estante buscar. Eso es hashing. Es una forma de acotar al instante dónde debe vivir o buscarse un dato, sin tener que revisar todo: vas directo al lugar correcto. Es lo que hace que buscar cosas en programas se sienta instantáneo, ya sea encontrar un archivo, verificar una contraseña o sacar datos de memoria.

La demo muestra una tabla hash con cinco cubetas y 10 palabras en cada una. Haz clic en "Obtener palabra al azar" para elegir una palabra y luego "Buscar" para ver en qué cubeta está. Solo revisamos esa cubeta, no las demás, así que encontrarla sigue siendo rápido.

Hashing: en qué cubeta cae una clave

Tabla hash con cinco cubetas y claves de ejemplo; escribe una clave y encuentra su cubeta.

Escribe una clave y haz clic en "Buscar" para ver el hash y la cubeta. Solo miramos esa cubeta, no toda la tabla.

Ocho paradigmas, algoritmos incontables

No importa cuántos algoritmos concretos te encuentres, casi siempre se apoyan en un conjunto chico de paradigmas: fuerza bruta, divide y vencerás, programación dinámica, greedy, backtracking, búsqueda, ordenamiento y hashing. Cada paradigma es una forma distinta de diseñar una solución: un patrón que eliges antes de escribir un bucle. Cuando los reconoces, dejas de ver solo código y empiezas a ver la idea de fondo. Ese cambio es lo que convierte seguir recetas en diseño de algoritmos de verdad.

Ocho formas de pensar. Incontables formas de programar.

Referencias & lecturas adicionales

¿Aún no tienes suficiente de Lumænaut_? Lee esto...