Puedes desarrollar microservicios y conocer todos los niveles del modelo OSI, pero ¿qué clase de programador eres si no puedes explicarle a un niño qué es un algoritmo?
A veces, preguntas muy simples sobre una profesión dejan perplejos incluso a los especialistas más experimentados. Esto es lo que ocurre, por ejemplo, cuando a un desarrollador con 5 o 10 años de experiencia se le pregunta: «¿Qué es un algoritmo?«.
Pero estamos aquí para dar respuestas claras a preguntas «tontas». En este artículo, te explicaremos qué son los algoritmos, para qué sirven y qué tipos existen.
¿Qué es un Algoritmo?
En un sentido amplio, un algoritmo es una secuencia de acciones que deben ejecutarse para obtener un determinado resultado.
Utilizamos algoritmos con frecuencia en nuestra vida diaria. Por ejemplo, cuando queremos preparar café en una cafetera de cápsulas, seguimos aproximadamente el siguiente algoritmo:
- Introducir la cápsula.
- Comprobar el nivel de agua en el depósito correspondiente.
- Si no hay suficiente agua, rellenarla.
- Colocar la taza bajo el surtidor de la cafetera.
- Poner en marcha la cafetera.
- Apagar la cafetera cuando la taza esté llena.
- Sacar la taza.
Si no se altera el orden de los pasos, cualquiera puede disfrutar de una taza de café caliente siguiendo estas instrucciones. Basta con saber cómo insertar la cápsula y encender/apagar la cafetera.
Con los ordenadores es mucho más complicado. No saben lo que significa «introducir una cápsula«, «echar agua«, «poner en marcha la cafetera«, etc. Para programar un robot barista para un modelo concreto de electrodoméstico, habría que describir el algoritmo con más detalle:
- Coge el enchufe del cable de alimentación de la cafetera.
- Enchufa el cable a la toma de corriente.
- Comprueba si hay agua en el depósito de agua.
- Si no hay suficiente agua:
4.1. Abre la tapa del depósito.
4.2. Coge la jarra de agua.
4.3. Vierte agua de la jarra al depósito hasta que se llene.
4.4. Cierra la tapa del depósito.
4.5. Deja la jarra de agua en la mesa. - Abre la tapa de la cafetera.
- Coge una cápsula de café de la caja.
- Introduce la cápsula en el compartimento de la cápsula.
- Cierra la tapa de la cafetera.
- Gira la palanca de la cafetera hacia la derecha.
- Cuando la taza esté llena, gira la palanca de la cafetera hacia la izquierda.
- Coge la taza.
- Lleva la taza al propietario.
Por supuesto, si estamos construyendo un robot desde cero, incluso este nivel de detalle no sería suficiente. Cada procedimiento tendría que implementarse en un lenguaje de programación (como C++ o Python), lo que en sí mismo no es tarea fácil. Sin embargo, la descripción se ha vuelto más precisa y formal.
Desde un punto de vista científico, la definición de algoritmo que hemos dado anteriormente no es del todo exacta. Después de todo, no todas las secuencias de acciones que conducen a un resultado pueden llamarse algoritmo.
Un algoritmo en informática es un conjunto de reglas comprensibles para el ejecutor para resolver un conjunto específico de tareas, que recibe datos de entrada y devuelve un resultado en un tiempo finito.
¿Para qué Sirven los Algoritmos?
Los algoritmos tienen dos cualidades notables: permiten resolver problemas de forma eficaz y evitan que tengamos que inventar soluciones que otros ya han encontrado. Esto se aplica tanto a la vida cotidiana como a las tecnologías de la información.
No es necesario reinventar la rueda
Imagina que estás solicitando un pasaporte. Si lo haces todo tú mismo y sin instrucciones, pasarás unos 40 minutos solo averiguando qué documentos necesitas y cuál es el procedimiento de solicitud. Es mucho más fácil utilizar un servicio online, porque el algoritmo ya está ahí: haces lo que te dicen y esperas el resultado. Y aún más fácil es recurrir a un intermediario que prepare todos los documentos y tramite el pasaporte por ti en una semana.
Este es un ejemplo muy cotidiano, pero así es como funciona la programación. Los desarrolladores estudian los algoritmos para escribir código rápido y eficiente: identifican un problema típico y seleccionan el algoritmo óptimo para él.
Digamos que necesitas ordenar en orden ascendente los números de una lista de 1000 elementos. Puedes recorrer la lista 1000 veces: en cada iteración, encontrar el número más pequeño y moverlo al principio de la lista. En este caso, el número total de pasos será de 1.000.000; un ordenador moderno puede hacerlo en un segundo.
¿Y si necesitas ordenar una matriz de 10.000.000 de elementos? Entonces, el ordenador tendría que realizar 1014 pasos, lo que llevaría mucho más tiempo. ¡Hay que optimizar!
Un desarrollador que no esté versado en informática empezará a devanarse los sesos para encontrar una solución más eficiente. Un profesional experimentado, en cambio, aplicará el algoritmo de ordenación rápida, que en el caso medio dará un «tiempo» de 16 × 107 pasos.
Los algoritmos permiten resolver problemas «irresolubles»
Ahora imagina que vives en el siglo XX en algún lugar de España y te ganas la vida viajando de ciudad en ciudad vendiendo batidoras. Para ahorrar tiempo y dinero, necesitas idear la ruta más corta que te permita visitar cada ciudad al menos una vez y volver.
Este es el famoso problema del viajante, para el que es prácticamente imposible encontrar la mejor solución. La simple enumeración no ayudará aquí. Con solo 10 ciudades, el número de rutas posibles sería de 3,6 millones, y con 26, incluso los ordenadores más potentes necesitarían varios miles de millones de años para recorrer todas las opciones.
Sin embargo, millones de dispositivos resuelven este problema cada día: los smartphones trazan rutas entre ciudades y los routers calculan la ruta óptima para los paquetes de la red. La cuestión es que existen algoritmos especiales que dan un resultado no ideal, pero sí bastante eficaz. Y hay que conocerlos si quieres trabajar en empresas que crean proyectos complejos e interesantes.
Propiedades de los Algoritmos
El informático y autor de libros de texto clásicos sobre programación Donald Knuth identificó las siguientes propiedades de los algoritmos:
- Finito
- Definido
- Que tenga entrada
- Que tenga salida o resultado
- Universalidad
- Eficiencia
Veamos cada una de ellas en detalle.
Finito. Un algoritmo debe resolver un problema en un número finito de pasos. La necesidad de este criterio es obvia: un programa que resuelve un problema indefinidamente nunca producirá un resultado.
Definido. El ejecutor (ordenador, sistema operativo) debe interpretar de forma inequívoca y correcta cada paso del algoritmo.
Que tenga entrada. Al igual que una función matemática, el resultado de un algoritmo depende de sus datos de entrada. Por ejemplo, la entrada de un algoritmo de ordenación es una matriz de números. Y una función que calcula un factorial toma un número natural.
Que tenga salida, o resultado. Un algoritmo debe producir un resultado específico. Por ejemplo, si buscamos una subcadena dentro de una cadena y esa subcadena está presente en ella, entonces debemos obtener la posición de esa subcadena como salida. Si no existe tal subcadena, el algoritmo debe devolver un valor apropiado, como -1.
Universalidad. Un algoritmo debe resolver problemas con diferentes datos de entrada. Por ejemplo, una buena función para ordenar matrices debe funcionar igual de bien con matrices de 10, 100 y 1.000.000 de elementos.
Eficiencia. Este requisito viene dictado por los recursos limitados de los ordenadores. En los albores de la informática, cada segundo de tiempo de procesador, cada byte de memoria, contaban. Y aunque los ordenadores modernos son mucho más potentes que sus predecesores, también pueden «ralentizarse» debido a algoritmos ineficientes.
Pseudocódigo: Cómo Escribir un Algoritmo sin Lenguaje de Programación
Imagina que has aprendido un lenguaje de programación, como Go, y has conseguido un trabajo como desarrollador backend en una empresa de TI. En tu equipo, además de desarrolladores backend, hay desarrolladores frontend que escriben código en JavaScript.
Se te ha ocurrido un algoritmo genial que acelerará tu aplicación y quieres contárselo a tus compañeros. Pero, ¿cómo hacerlo si programan en otro lenguaje?
Para estas situaciones existe el pseudocódigo. Permite expresar la lógica de un programa utilizando comandos comprensibles para todos, sin profundizar en los detalles de implementación de un lenguaje concreto. En la literatura académica, los algoritmos se describen principalmente mediante pseudocódigo.
No existen estándares universalmente aceptados para el pseudocódigo, y los autores utilizan sus propias notaciones originales. Aunque a menudo toman prestados nombres de operaciones de Python, Pascal y Java. Por ejemplo, el código siguiente se asemeja a un programa en Python:
int linear_search(int[] arr, int x):
if arr is empty:
return -1
for i in 0..n:
if arr[i] == x:
return i
return -1
El pseudocódigo también puede escribirse en lenguaje natural, como en los libros de texto escolares de informática:
FUNCIÓN búsqueda_lineal(entero[] array, entero x):
SI array VACÍO:
DEVOLVER -1
PARA i EN RANGO DESDE 0 HASTA LONGITUD(array):
SI array[x] IGUAL A x:
DEVOLVER i
DEVOLVER -1
Lo principal es que quien lea tu algoritmo lo entienda y pueda reproducirlo en su lenguaje de programación.
¿Qué son los Diagramas de Flujo o Cómo Dibujar un algoritmo?
Si has asistido a clases de informática en la escuela, seguro que has dibujado y leído diagramas de flujo. Si no es así, debes saber que los algoritmos pueden describirse no solo verbalmente, sino también gráficamente.
Los diagramas de flujo son figuras geométricas conectadas por flechas. Óvalos, rectángulos, rombos y otras figuras representan pasos individuales del algoritmo, y las flechas indican la dirección del flujo de datos. Cada bloque contiene un comando en forma de expresión lógica o matemática.
En la tabla siguiente se muestran los elementos básicos de los diagramas de flujo:
Imagen | Significado | Elemento de código en Python |
---|---|---|
Inicio/fin del programa | No se indica o se indica como el inicio de una función: def foo(x): # código | |
Entrada/salida de datos | Operadores de entrada y salida:print("¡Hola!") word = input() | |
Operaciones aritméticas | Operadores aritméticos:100 - 10 | |
Condición | Operador condicional:if n < 5: | |
Bucle con contador | Bucle for:for k, v in enumerate(arr): | |
Entrada/salida en un archivo | Funciones para trabajar con archivos:f = open("text.txt", 'r') |
Con este sencillo conjunto de figuras, puedes dibujar un diagrama de casi cualquier algoritmo.
Los diagramas de flujo pueden dibujarse en Microsoft Visio y en Google Docs (Insertar → Dibujo → Nuevo +). También existen servicios especiales, como el Draw.io en la nube y las aplicaciones de escritorio Dia e yEd.
Ahora veamos qué tipos de algoritmos existen, escribamos ejemplos en Python y dibujemos diagramas de flujo para ellos.
Tipos de Algoritmos y Ejemplos
Los algoritmos pueden dividirse en varios grupos según su construcción.
Algoritmos lineales
En los algoritmos lineales, las acciones se suceden una tras otra. Estos programas son los más sencillos, pero rara vez se encuentran en la práctica.
Ejemplo. Escribe un programa que multiplique un número introducido por el usuario por 100 e imprima el resultado en la pantalla.
La secuencia de acciones ya está establecida en la tarea: introducir un número → multiplicar por 100 → imprimir el resultado. Traduzcamos esto al lenguaje de los diagramas de flujo:
A continuación se muestra la implementación del algoritmo en Python:
x = int(input())
x = x * 100
print(x)
>>> 5
>>> 500
Algoritmos de ramificación
En los algoritmos de ramificación, el flujo del programa depende del valor de una expresión lógica en el bloque «Condición«. En esencia, cualquier expresión lógica se reduce a una elección entre verdadero (True, «1») o falso (False, «0»).
Ejemplo. Escribe un programa que pida al usuario su edad. Si es igual o superior a 18 años, el programa muestra un saludo, incrementa el valor del contador de visitantes en 1 y se despide, y si es inferior a 18 años, se despide inmediatamente y termina.
Para representar el curso de la solución, utilizaremos un bloque condicional. En todos los diagramas se representa mediante un rombo con la condición inscrita:
Lo mismo en Python:
visits_counter = 0
answer = int(input("¿Cuántos años tienes? "))
if answer >= 18:
print("¡Bienvenido!")
visits_counter += 1
else:
print("Acceso denegado")
Cuando el usuario introduce 18 o más, el programa ejecuta la parte del código que está escrita bajo el operador if. Si la edad es inferior a 18 años, se muestra en pantalla el mensaje «Acceso denegado» y el programa finaliza.
Algoritmos cíclicos
Estos algoritmos contienen bucles, conjuntos de acciones que se ejecutan varias veces. El número de repeticiones puede especificarse mediante un entero o una condición. En algunos casos, como en sistemas operativos y firmware de microcontroladores, se utilizan bucles infinitos.
Ejemplo. Escribe un programa que incremente cíclicamente el valor de un contador en 1 e imprima su valor en cada paso. Cuando el valor del contador alcance 10, el programa debería terminar.
Nuestra solución se basará en la siguiente condición: si el valor del contador es inferior a 10, sumar 1; en caso contrario, terminar. Así es como se ve en forma de diagrama de flujo:
Traduzcamos esto a código Python. Observa que no escribimos una rama separada para el caso «No»:
count = 0
#añadir 1 a count mientras count sea menor que 10
while count < 10:
count += 1
print(count)
print("¡La variable count es igual a 10!")
Resultado del programa:
1
2
3
4
5
6
7
8
9
10
Algoritmos recursivos
La recursión es un fenómeno en el que un sistema se llama a sí mismo, pero con diferentes datos de entrada. Estos algoritmos se utilizan para recorrer diccionarios en profundidad, calcular factoriales, calcular potencias y otras tareas prácticas. En general, todo esto puede hacerse con bucles, pero el código de las funciones recursivas es más conciso y legible.
Ejemplo. El usuario introduce un número n
. Calcula su factorial e imprime el resultado en la pantalla.
#función que se llama a sí misma
def factorial(n):
if n == 1:
return 1
#cuando la función devuelve un valor,
#se llama a sí misma, pero con el argumento n - 1
return n * factorial(n - 1)
Así es como se ve el diagrama de flujo de un algoritmo recursivo:
En la práctica, los algoritmos puramente secuenciales, condicionales o cíclicos son raros, pero juntos permiten crear soluciones de cualquier complejidad.
Existen otras clasificaciones de algoritmos. Por ejemplo, según el conjunto de problemas que resuelven, pueden dividirse en numéricos, de búsqueda, de ordenación, de cadenas, de red y criptográficos. Y en función de la precisión de los resultados obtenidos, en normales y estocásticos (probabilísticos).
¿Qué Sigue?
Si quieres profundizar en el estudio de los algoritmos, empieza por libros sencillos y amenos sobre informática:
Portada del Libro | Título y Autor | Enlace de Compra |
---|---|---|
Algoritmos. Guía Ilustrada para Programadores y Curiosos – Aditya Y. Bhargava | Comprar | |
Pseudocódigo Para Principiantes – Carlos Pes | Comprar | |
Algoritmos – Panos Louridas | Comprar | |
Algoritmos Fundamentales – Knuth Donald E | Comprar | |
Estructuras de datos y algoritmos – Mariona Nadal | Comprar | |
Algoritmos: Guía práctica – Andy Vickler | Comprar | |
Algoritmos correctos y eficientes – Narciso Martí Oliet | Comprar | |
Algoritmos iluminados – Tim Roughgarden | Comprar | |
Fundamentos de algoritmia – Paul Bratley | Comprar |
Cuando te familiarices con los algoritmos básicos y aprendas a resolver problemas estándar con ellos, pasa a una literatura más seria. Por ejemplo, lee «Algorithms» de Robert Sedgewick y «Introduction to Algorithms» de Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest y Clifford Stein.
«Yandex» ofrece ejemplos de análisis de problemas algorítmicos y errores comunes. Y puedes practicar, consolidar la teoría y prepararte para entrevistas técnicas en LeetCode, donde encontrarás cientos de problemas de diversa complejidad y para diferentes lenguajes de programación.