Entender los principios del diseño de algoritmos

Este artículo se sumergirá en los principios del diseño de algoritmos. Si no tienes idea de a qué me refiero, sigue leyendo.!

Cuando escuchas la palabra "algoritmo", probablemente respondas de una de estas tres formas:

  1. Inmediatamente sabes y entiendes de qué estamos hablando porque estudiaste ciencias de la computación..
  2. Usted sabe que los algoritmos son los caballos de batalla de compañías como Google y Facebook, pero no están realmente seguros de lo que significa la palabra..
  3. Corres y te escondes en el miedo porque todo lo que sabes sobre algoritmos te recuerda las pesadillas de Cálculo en la escuela secundaria.

Si eres uno de los dos siguientes, este artículo es para ti..


¿Qué es un algoritmo, exactamente?

Los algoritmos no son un tipo especial de operación, necesariamente. Son conceptuales, un conjunto de pasos que se toman en el código para alcanzar un objetivo específico..

Los algoritmos se han definido comúnmente en términos simples como "instrucciones para completar una tarea". También se les ha llamado "recetas". En La red social, Un algoritmo es lo que Zuckerberg necesitaba para hacer funcionar Facemash. Si viste la película, probablemente recuerdes haber visto lo que parecía una ecuación garabateada en una ventana del dormitorio de Mark. Pero, ¿qué tiene que ver ese álgebra de garabatos con el simple sitio "hot or not" de Mark??

Los algoritmos son de hecho instrucciones. Quizás una descripción más precisa sería que los algoritmos son patrones para completar una tarea de una manera eficiente. Facemash de Zuckerberg era un sitio de votación para determinar el atractivo de alguien en relación con todo un grupo de personas, pero al usuario solo se le darían opciones entre dos personas. Mark Zuckerberg necesitaba un algoritmo que decidiera qué personas se unían entre sí, y cómo valorar un voto en relación con la historia previa de esa persona y los candidatos anteriores. Esto requería más intuición que simplemente contar los votos para cada persona..

Por ejemplo, digamos que desea crear un algoritmo para sumar 1 a cualquier número negativo, y restar 1 de cualquier número positivo, y no hacer nada con 0. Puede hacer algo como esto (en el pseudo código de JavaScript):

función addOrSubtractOne (número) si (número < 0)  return number + 1  else if (number < 0)  return number - 1  else if (number == 0)  return 0;  

Puede que te digas a ti mismo: "Esa es una función". Y tienes razón. Los algoritmos no son un tipo especial de operación, necesariamente. Son conceptuales: un conjunto de pasos que debe seguir en el código para alcanzar un objetivo específico..

Entonces, ¿por qué son un gran problema? Claramente, sumar o restar 1 a un número es una cosa bastante simple de hacer.

Pero hablemos por un segundo sobre la búsqueda. Para buscar un número en una serie de números, ¿cómo pensarías hacerlo? Un enfoque ingenuo sería iterar el número, comparando cada número con el que está buscando. Pero esta no es una solución eficiente, y tiene un rango muy amplio de tiempos de finalización posibles, lo que la convierte en un método de búsqueda errático y poco confiable cuando se escala a grandes conjuntos de búsqueda..

función naiveSearch (aguja, pajar) para (var i = 0; i < haystack.length; i++) if (haystack[i] == needle)  return needle;   return false; 

Afortunadamente, podemos hacerlo mejor que esto para la búsqueda..

¿Por qué es ineficiente??

No hay mejor manera de convertirse en un mejor diseñador de algoritmos que tener un profundo conocimiento y apreciación de los algoritmos..

Digamos que su matriz tiene 50,000 entradas y la búsqueda de fuerza bruta (es decir, la búsqueda mediante la iteración de la matriz completa). La entrada que está buscando, en el mejor de los casos, será la primera entrada en la matriz de 50,000 entradas. Sin embargo, en el peor de los casos, el algoritmo tardará 50,000 veces más en completarse que en el mejor de los casos..

Entonces, ¿qué es mejor??

En su lugar, buscarías usando la búsqueda binaria. Esto implica ordenar la matriz (sobre la que le permitiré aprender por su cuenta) y, posteriormente, dividir la matriz por la mitad, y verificar si el número de búsqueda es mayor o menor que la marca de la mitad de la matriz. Si es mayor que la marca de la mitad de una matriz ordenada, entonces sabemos que la primera mitad se puede descartar, ya que el número buscado no forma parte de la matriz. También podemos recortar mucho trabajo definiendo los límites externos de la matriz y verificando si el número buscado existe fuera de esos límites, y si es así, tomamos lo que habría sido una operación de múltiples iteraciones y lo convertimos en una sola operación de iteración (que en el algoritmo de fuerza bruta habría tomado 50,000 operaciones).

sortedHaystack = recursiveSort (haystack); función bBuscar (aguja, ordenadoHaystack, firstIteration) si (firstIteration) si (aguja> sortedHaystack.last || aguja < sortedHaystack.first) return false;   if (haystack.length == 2) if (needle == haystack[0])  return haystack[0];  else  return haystack[1];   if (needle < haystack[haystack.length/2]) bSearch(needle, haystack[0… haystack.length/2 -1], false);  else  bSearch(needle, haystack[haystack.length/2… haystack.length], false);  

Suena bastante complicado

Tome la naturaleza aparentemente complicada de un único algoritmo de búsqueda binario y aplíquelo a miles de millones de posibles enlaces (como buscar en Google). Más allá de eso, apliquemos algún tipo de sistema de clasificación a esas búsquedas vinculadas para dar un orden de páginas de respuesta. Mejor aún, aplique algún tipo de sistema de "sugerencia" aparentemente aleatorizado basado en modelos sociales de inteligencia artificial diseñados para identificar a quién le gustaría agregar como amigo.

Esto nos da una comprensión mucho más clara de por qué los algoritmos son más que un nombre elegante para las funciones. En su máxima expresión, son formas inteligentes y eficientes de hacer algo que requiere un mayor nivel de intuición que la solución más aparente. Pueden tomar lo que podría requerir un año de supercomputadora y convertirlo en una tarea que termina en segundos en un teléfono móvil..

¿Cómo se aplican los algoritmos a mí??

Para la mayoría de nosotros como desarrolladores, no estamos diseñando algoritmos abstractos de alto nivel diariamente..

Por suerte, nos apoyamos en los desarrolladores que vinieron antes que nosotros, que escribieron funciones de ordenación nativas y nos permiten buscar cadenas para subcadenas con indexOf de manera eficiente..

Pero HACEMOS, sin embargo, tratamos con algoritmos propios. Nosotros creamos para Bucles y funciones de escritura todos los días; Entonces, ¿cómo pueden los buenos principios de diseño de algoritmos informar la escritura de estas funciones?

Conozca su entrada

Uno de los principios principales del diseño algorítmico es, si es posible, construir su algoritmo de tal manera que la entrada haga parte del trabajo por usted. Por ejemplo, si sabe que su entrada siempre será números, no necesita tener excepciones / verificaciones para cadenas, o coaccionar sus valores en números. Si sabe que su elemento DOM es el mismo cada vez que en un para bucle en JavaScript, no debería consultar ese elemento en cada iteración. En el mismo token, en tu para bucles, no debe usar funciones de conveniencia con sobrecarga si puede lograr lo mismo usando (más cerca) operaciones simples.

// no hagas esto: para (var i = 1000; i> 0; i -) $ ("# foo"). append ("bar"); // haga esto en su lugar var foo = $ (" # foo "); var s =" "; para (var i = 1000; i> 0; i -) s + ="bar"; foo.append (s);

Si usted es un desarrollador de JavaScript (y utiliza jQuery) y no sabe qué hacen las funciones anteriores y en qué se diferencian significativamente, el siguiente punto es para usted..

Entiende tus herramientas

En su máxima expresión, los [algoritmos] son ​​formas inteligentes y eficientes de hacer algo que requiere un mayor nivel de intuición que la solución más aparente..

Es fácil pensar que no hace falta decirlo. Sin embargo, hay una diferencia entre "saber cómo escribir jQuery" y "entender jQuery". Comprender sus herramientas significa que comprende lo que hace cada línea de código, tanto de inmediato (el valor de retorno de una función o el efecto de un método) como implícitamente (la sobrecarga que se asocia con la ejecución de una función de biblioteca, o cuál es la más eficiente). método para concatenar una cadena). Para escribir excelentes algoritmos, es importante conocer el rendimiento de las funciones o utilidades de nivel inferior, no solo el nombre y la implementación de las mismas..

Entender el medio ambiente

Diseñar algoritmos eficientes es un compromiso de compromiso total. Además de comprender sus herramientas como una pieza independiente, también debe comprender la forma en que interactúan con el sistema más grande que se tiene a mano. Por ejemplo, para comprender JavaScript en una aplicación específica en su totalidad, es importante comprender el DOM y el rendimiento de JavaScript en los escenarios de varios navegadores, cómo la memoria disponible afecta la velocidad de representación, la estructura de los servidores (y sus respuestas) con los que puede estar interactuando. así como una miríada de otras consideraciones que son intangibles, tales como escenarios de uso.


Reduciendo la carga de trabajo

En general, el objetivo del diseño de algoritmos es completar un trabajo en menos pasos. (Hay algunas excepciones, como el hash Bcrypt). Cuando escriba su código, tenga en cuenta todos De las operaciones simples que la computadora está tomando para alcanzar la meta. Aquí hay una lista de verificación simple para comenzar en un camino hacia un diseño de algoritmo más eficiente:

  • Utilice las funciones de idioma para reducir las operaciones (almacenamiento en caché de variables, encadenamiento, etc.).
  • Reducir lo más posible el anidamiento de bucle iterativo.
  • Definir variables fuera de los bucles cuando sea posible.
  • Utilice la indexación automática de bucles (si está disponible) en lugar de la indexación manual.
  • Utilice técnicas de reducción inteligentes, como la optimización recursiva de división y conquista y consulta, para minimizar el tamaño de los procesos recursivos.

Estudiar tecnicas avanzadas

No hay mejor manera de convertirse en un mejor diseñador de algoritmos que tener un profundo conocimiento y apreciación de los algoritmos..

  • Tómese una hora o dos cada semana y lea El arte de la programación de computadoras..
  • Pruebe un desafío de programación de Facebook o un Google Codejam.
  • Aprende a resolver el mismo problema con diferentes técnicas algorítmicas..
  • Desafíate a ti mismo implementando funciones integradas de un lenguaje, como .ordenar(), con operaciones de nivel inferior.

Conclusión

Si no sabía qué era un algoritmo al comienzo de este artículo, es de esperar que ahora tenga una comprensión más concreta del término un tanto esquivo. Como desarrolladores profesionales, es importante que entendamos que el código que escribimos se puede analizar y optimizar, y es importante que nos tomemos el tiempo para hacer este análisis del rendimiento de nuestro código..

¿Algún algoritmo divertido de práctica has encontrado problemas? Tal vez una programación dinámica "problema mochila", o "paseo borracho"? O quizás conoce algunas de las mejores prácticas de recursión en Ruby que difieren de las mismas funciones implementadas en Python. Compártelos en los comentarios.!