miércoles, 14 de agosto de 2024

7 Usos de los Algoritmos en la Vida Real

 Me he encontrado varias veces con esta pregunta "¿Los algoritmos sirven en la vida real?", principalmente por 2 fuentes diametralmente opuestas:

  1. Gente de programación competitiva que se pregunta si todo éso le va a servir en su vida profesional, al sentirse un tanto intimidados por la cantidad de cosas que desconocen.
  2. Gente enojada porque dicen que hay demasiados algoritmos en las entrevistas técnicas de programación.
Respuesta corta: Sí sirven.
Respuesta larga:

Aunque sí sirvan, dista mucho de ser como en la programación competitiva:
  • Hay algunas cosas de la algoritmia que sirven más que otras; las cosas más útiles en la programación competitiva no siempre son tan útiles de conocer profesionalmente (en particular cosas relacionadas al multi-threading, y optimizaciones en la compilación resultan altamente efectivas a pesar de que en programación competitiva son casi ignoradas)
  • La gran mayoría de las optimizaciones aplicables a la vida real utilizan ideas mucho más sencillas que cualquier problema de IOI
  • Con la mayoría de los algoritmos más avanzados que pudieran ser útiles, resulta que es mejor usar la implementación de alguien más en lugar de reimplementarlos.
...y sobre los problemas de entrevista, yo pienso que su grado de complejidad es el correcto: aunque nunca programemos listas enlazadas o árboles binarios directamente, recorrer todos los campos de un objeto json o de una estructura de directorios son tareas comunes, y creo más razonable esperar que el candidato se pueda ajustar a usar una estructura super genérica en lugar de usar un framework en específico, que seguramente sería más complicado (además de que no es necesario llegar a la solución optima del problema, lo que se evalúa es mayoritariamente otras cosas).

Ahora, seguramente con esta información se están preguntando, si sí son útiles los algoritmos pero no de la forma de programación competitiva, entonces, ¿de qué manera son útiles?. Pues aquí les traigo unos cuantos ejemplos (los intentaré ordenar por qué tan frecuente/útil aparece el ejemplo, no por qué tan emocionante sea).

1. Análisis de Complejidad

Si bien esto no es saberse un algoritmo en particular, el análisis de complejidad es fundamental y hay que estarlo usando todo el tiempo.
No es raro que quienes no estén muy familiarizados con él, inadvertidamente produzcan cuellos de botella. A veces con cosas tan simples como utilizar un array.find dentro de un bucle y a veces con cosas menos obvias, como realizar demasiadas consultas a una base de datos, o saltarse la parte de la documentación de una biblioteca donde menciona el rendimiento.
En general siempre que se está planenado un proyecto hay que preguntarse cuáles van a ser los requerimientos y elegir el algoritmo que se ajuste mejor a dichos requerimientos.
Generalmente será un algoritmo que ya fue implementado por alguien más, pero poder elegir desde un momento temprano qué framework o bibliotecas usar puede ser determinante para saber si el proyecto será un éxito o no y éso requiere análisis de complejidad.

2. Memorización

El truco de memorizar las llamadas computacionalmente pesadas para no tener que recalcularlas es súmamente útil, y al combinarse con el análisis de complejidad es posible saber donde aplicarlo para obtener resultados palpables.
La gran diferencia es que la memorización suele utilizarse menos para evitar repetir cálculos grandes y más para evitar repetir llamadas a red o lecturas de archivos.

3. Recursividad

La recursividad es especialmente útil cuando se requiere recorrer árboles, y en programación hay árboles en todos lados: un objeto Json es un árbol, el sistema de archivos es otro árbol, los mismos documentos HTML y los CSS son árboles y también en los videojuegos hay muchos árboles involucrados (desde las estructuras de componentes, hasta la estructura jerárquica para simular cinemática directa o calcular cinemática inversa).

4. Programación Asíncrona

Es común que varios de los procesos con los que se trabaja sean asíncronos (es decir, no se realizan de manera inmediata sino que requieren de esperar y durante ése tiempo el programa puede continuar ejecutándose).
Prácticamente todos los lenguajes de programación modernos tienen cierto soporte para asincronía.
La manera en la que se cordinan dichas tareas puede tener un gran impacto en el rendimiento final del programa.
Para poder realizar todas estas tareas efectivamente hay que poder analizar los algoritmos y saber en qué momentos es conveniente esperar a una respuesta, y cómo ir procesando los resultados a medida que llegan.

5. Multi-threading

Esto también es parte de la programación asíncrona pero va un paso mas allá, en este caso varios hilos corren a la vez, cada uno ejecutando su propio pedazo de código y es necesario ser capaz de idear maneras de que los hilos trabajen juntos y no produzcan bugs en el momento que interactúan con esturcturas de datos que comparten.
Esto requiere un diseño minucioso de estructuras de datos para ver cómo evitar lo más posible bloquear hilos sin sacrificar mucho rendimiento.
También está el detalle de que todo lo que es sólo lectura es seguro para multi-threading, pero actualizar una estructura que es sólo lectura sin necesidad de copiar la estructura completa requiere técnicas que se utilizan bastante en la persistencia.

6. Geometría Computacional

Tanto en mi trabajo desarrollando videojuegos como en mi trabajo en un buscador de hoteles necesité usar geometría computacional extensivamente.

7. Máscaras de Bits

A veces resultan útiles para ahorrar memoria o para hacer algunas operaciones con las que normalmente se usarían arreglos booleanos pero de una forma más simple y además más eficiente.  También varias APIs las usan extensivamente para evitar pasar demasiados parámetros booleanos.

No hay comentarios.:

Publicar un comentario

De StackOverflow a ChatGPT: El Arte de Pegar sin Pensar

 Es curioso cómo apenas en Agosto del año pasado critiqué duramente la práctica de copiar código de páginas de internet para pegarlo dentro ...