En algoritmos y complejidad, nos centramos en la complejidad asintótica de los algoritmos, es decir, la cantidad de recursos que utiliza un algoritmo a medida que el tamaño de la entrada llega al infinito.
En la práctica, lo que se necesita es un algoritmo que funcione rápidamente en un número finito (aunque posiblemente muy grande) de instancias.
Un algoritmo que funciona bien en la práctica en el número finito de instancias que nos interesa no necesita tener una buena complejidad asintótica (el buen desempeño en un número finito de instancias no implica nada con respecto a la complejidad asintótica). Del mismo modo, un algoritmo con buena complejidad asintótica puede no funcionar bien en la práctica en el número finito de instancias que nos interesan (por ejemplo, debido a las constantes grandes).
¿Por qué utilizamos la complejidad asintótica? ¿Cómo se relacionan estos análisis asintóticos con el diseño de algoritmos en la práctica?
Respuestas:
La pregunta interesante es: ¿cuál es la alternativa? El único otro método que conozco es probar / comparar. Programamos los algoritmos, dejamos que se ejecuten en (una muestra representativa de) el conjunto de entrada finita y comparamos los resultados. Hay un par de problemas con eso.
Dicho esto, ignorar todo tipo de efectos y constantes en el análisis es típico, pero puede llamarse perezoso (con respecto a la práctica). Sirve para comparar ideas algorítmicas más que para determinar el rendimiento de una implementación dada (incluso pseudocódigo). Es bien sabido por la comunidad que esto es grosero y que a menudo es necesaria una mirada más cercana; por ejemplo, Quicksort es menos eficiente que el tipo de inserción para (muy) pequeñas entradas. Para ser justos, un análisis más preciso suele ser difícil ².
Otra justificación a posteriori del punto de vista formal y abstracto es que, en este nivel, las cosas son a menudo más claras. Por lo tanto, décadas de estudio teórico han dado lugar a una gran cantidad de ideas algorítmicas y estructuras de datos que son útiles en la práctica. El algoritmo teóricamente óptimo no siempre es el que desea usar en la práctica; hay otras consideraciones, pero el rendimiento debe hacerse; piense en los montones de Fibonacci, y es posible que esta etiqueta ni siquiera sea única. Es difícil para un programador típico preocupado por optimizar las expresiones aritméticas que se le ocurra una nueva idea en este nivel (por no decir que no sucede); Sin embargo, ella puede (y debe) realizar esas optimizaciones sobre la idea asimilada.
Hay herramientas formales y teóricas para cerrar la brecha y practicar hasta cierto punto. Ejemplos son
Por ejemplo, Knuth es conocido por contar literalmente el número de declaraciones diferentes (para una implementación dada en un modelo dado), lo que permite una comparación precisa de algoritmos. Ese enfoque es imposible en un nivel abstracto y difícil de hacer en modelos más complejos (piense en Java). Ver [4] para un ejemplo moderno.
Siempre habrá una brecha entre la teoría y la práctica. Actualmente estamos trabajando en una herramienta³ con el objetivo de combinar lo mejor de ambos mundos para hacer predicciones sólidas tanto para los costos algorítmicos como para el tiempo de ejecución (en promedio), pero hasta ahora no hemos podido eliminar escenarios en los que un algoritmo tiene mayor cuesta un tiempo de ejecución menor (en algunas máquinas) que uno equivalente (aunque podemos detectar eso y respaldar la búsqueda del motivo).
Recomiendo a los profesionales que utilicen la teoría para filtrar el espacio de los algoritmos antes de ejecutar puntos de referencia:
fuente
Supongo que esta pregunta surge de la enseñanza de un curso que incluye análisis asintótico. Hay varias respuestas posibles sobre por qué este material se enseña en clases introductorias:
El análisis asintótico es una abstracción matemática que se entrega al análisis. Como (posiblemente) matemáticos, queremos poder analizar algoritmos, y la única forma de domar su complejidad es usar análisis asintótico.
Evaluar el rendimiento asintótico de un algoritmo señala algunos principios que son útiles en la práctica: por ejemplo, concéntrese en esa parte del código que toma la mayor parte del tiempo y descarte cualquier parte del código que tome una parte del tiempo asintóticamente insignificante .
Algunas de las técnicas de análisis asintótico son útiles. Me refiero principalmente al llamado "teorema maestro", que en muchas circunstancias es una buena descripción de la realidad.
También hay una razón histórica: cuando las personas comenzaron a analizar algoritmos, pensaron sinceramente que la complejidad asintótica refleja el uso práctico. Sin embargo, finalmente se demostró que estaban equivocados. Lo mismo sucedió con P como la clase de problemas solucionables de manera eficiente, y NP como la clase de problemas intratables, los cuales son engañosos en la práctica.
Personalmente, creo que el análisis asintótico es una parte razonable del plan de estudios. Las partes más cuestionables incluyen la teoría del lenguaje formal y la teoría de la complejidad (cualquier cosa que tenga que ver con una máquina de Turing). Algunas personas argumentan que si bien estos temas no son útiles para el aspirante a programador per se, inculcan en ella un cierto pensamiento mental que es necesario para ser un buen practicante. Otros sostienen que la teoría a veces influye en la práctica, y estos casos raros son suficientes para justificar la enseñanza de estas materias bastante arcanas a la audiencia general de informática. Prefiero que aprendan historia o literatura, o cualquier otro tema en el que estén realmente interesados; ambos son tan relevantes para sus perspectivas laborales futuras, y más importantes para ellos como seres humanos.
fuente
Hay dos razones serias para usar el análisis asintótico de los tiempos de ejecución:
para permitir la trazabilidad matemática. Los casos en los que es posible encontrar expresiones exactas para el recuento de operaciones son excepcionales. Estudiar las asintóticas abre más posibilidades (como las aproximaciones asintóticas de funciones complicadas son útiles).
Y hay muchos otros (como la independencia de la máquina, el significado, la comparabilidad ...).
fuente
Como se señaló en la respuesta de Raphael, el cálculo exacto del peor tiempo de ejecución puede ser muy difícil. El cálculo exacto también puede ser innecesario ya que el modelo de RAM ya introduce aproximaciones. Por ejemplo, ¿todas las operaciones realmente toman el mismo tiempo? Las implementaciones específicas (hardware, optimizaciones) pueden acelerar un algoritmo por factores constantes. Queremos entender qué tan efectivo es un algoritmo independiente de estos factores. Esta es una gran motivación para el uso del análisis asintótico.
fuente
Porque los asintóticos son "simples" (bueno, más simple que hacer el análisis exacto para casos finitos, de todos modos).
Compare, por ejemplo, el enciclopédico "The Art of Computer Programming" de Knuth, que hace un análisis detallado de todos los algoritmos importantes (y muchos no tan importantes) con el análisis de la regla general que a menudo es suficiente para obtener una estimación asintótica ( o simplemente un límite), como se practica en la mayoría de los libros de "algoritmos".
Ciertamente tienes razón. Si el problema es lo suficientemente importante, puede justificarse un análisis de estilo Knuth (o quizás un poco menos detallado). En muchos casos, basta con una pista de la complejidad asintótica (quizás promedio con dispersión) ajustada a los datos experimentales. En la mayoría de los casos , hacer una clasificación aproximada de algoritmos competitivos, como una primera ronda de eliminación de malezas que compara las asintóticas, puede ser lo suficientemente preciso. Y si no hay contendientes, recibir las malas noticias del costo exacto en detalle es solo masoquismo.
fuente
Aquí, por análisis asintótico, supongo que queremos decir el comportamiento del algoritmo a medida que el tamaño de la entrada llega al infinito.
La razón por la que utilizamos el análisis asintótico es porque es útil para predecir el comportamiento de los algoritmos en la práctica . Las predicciones nos permiten tomar decisiones, por ejemplo, cuando tenemos diferentes algoritmos para un problema, ¿cuál debemos usar? (Ser útil no significa que siempre sea correcto).
La misma pregunta puede hacerse sobre cualquier modelo simplificado del mundo real. ¿Por qué utilizamos modelos matemáticos simplificados del mundo real?
Piensa en la física. La física newtoniana clásica no es tan buena como la física relativista para predecir el mundo real. Pero es un modelo lo suficientemente bueno para construir automóviles, rascacielos, submarinos, aviones, puentes, etc. Hay casos en los que no es lo suficientemente bueno, por ejemplo, si queremos construir un satélite o enviar una sonda espacial a Plutón o predecir el movimiento. de objetos celestes masivos como estrellas y planetas u objetos de muy alta velocidad como electrones. Es importante saber cuáles son los límites de un modelo.
Por lo general, es una aproximación suficientemente buena del mundo real. En la práctica, vemos a menudo que un algoritmo con un mejor análisis asintótico funciona mejor en la práctica. Rara vez se da el caso de que un algoritmo tenga un mejor comportamiento asintótico. Por lo tanto, si las entradas pueden ser lo suficientemente grandes, normalmente podemos confiar en el análisis asintótico como primera predicción del comportamiento de los algoritmos. No es así si sabemos que las entradas van a ser pequeñas. Dependiendo del rendimiento que queramos, es posible que tengamos que hacer un análisis más cuidadoso, por ejemplo, si tenemos información sobre la distribución de las entradas que se proporcionará el algoritmo, podemos hacer un análisis más cuidadoso para lograr los objetivos que tenemos (por ejemplo, rápido en 99 % de entradas). El punto es que, como primer paso, el análisis asintótico es un buen punto de partida. En la práctica, también debemos hacer pruebas de rendimiento, pero tenga en cuenta que también tiene sus propios problemas.
En resumen, la complejidad asintótica es una aproximación relativamente fácil de calcular de la complejidad real de los algoritmos para tareas básicas simples (problemas en un libro de texto de algoritmos). A medida que creamos programas más complicados, los requisitos de rendimiento cambian y se vuelven más complicados y el análisis asintótico puede no ser tan útil.
Es bueno comparar el análisis asintótico con otros enfoques para predecir el rendimiento de los algoritmos y compararlos. Un enfoque común son las pruebas de rendimiento contra entradas aleatorias o de referencia. Es común cuando calcular la complejidad asintótica es difícil o inviable, por ejemplo, cuando usamos la heurística como en la resolución de SAT. Otro caso es cuando los requisitos son más complicados, por ejemplo, cuando el rendimiento de un programa depende de factores externos y nuestro objetivo podría ser tener algo que termine dentro de algunos límites de tiempo fijos (por ejemplo, piense en actualizar la interfaz que se muestra a un usuario) en el 99% de los casos. entradas
Pero tenga en cuenta que el análisis de rendimiento también tiene sus problemas. No proporciona beneficiarios matemáticos sobre el rendimiento en menos, realmente ejecutamos la prueba de rendimiento en todas las entradas que se le darán al algoritmo (a menudo inviable desde el punto de vista computacional) (y a menudo no es posible decidir que algunas entradas nunca se darán). Si ponemos a prueba en contra de una muestra aleatoria o una referencia implícita estamos asumiendo cierta regularidad sobre el rendimiento de los algoritmos, es decir, el algoritmo llevará a cabo de manera similar en otros insumos que no formaban parte de la prueba de rendimiento.
El segundo problema con las pruebas de rendimiento es que dependen del entorno de prueba. Es decir, el rendimiento de un programa no está determinado solo por las entradas, sino por factores externos (por ejemplo, tipo de máquina, sistema operativo, eficiencia del algoritmo codificado, utilización de la CPU, tiempos de acceso a la memoria, etc.), algunos de los cuales pueden variar entre diferentes ejecuciones de La prueba en la misma máquina. Una vez más, estamos asumiendo que los entornos particulares en los que se llevan a cabo las pruebas de rendimiento son similares al entorno real a menos que hagamos las pruebas de rendimiento en todos los entornos en los que podemos ejecutar el programa (y cómo podemos predecir en qué máquinas alguien podría ejecutar una clasificación) algoritmo en 10 años?).
fuente
ahora imagine que la espera se repite en el código tantas veces como se llama al código. ¿Cómo cuantificar / justificar matemáticamente esta aparente superioridad del algoritmo de clasificación rápida? (es decir, ¿su nombre está realmente justificado o es solo un eslogan de marketing?) a través de mediciones de complejidad asintótica. uno se queda mirando las animaciones subjetivamente, sintiendo que la clasificación de burbujas es de alguna manera un algoritmo más débil y el análisis de complejidad asintótica puede probar esto cuantitativamente. pero tenga en cuenta que el análisis de complejidad asintótica es solo una herramienta en la bolsa de herramientas para analizar algoritmos y no siempre es la mejor.
y vale la pena mirar el código de lado a lado también. Bubblesort parece ser conceptualmente más simple y no utiliza la recursividad. quicksort no se comprende tan inmediatamente como el principio de pivote "mediana de 3". Bubblesort podría implementarse solo en bucles sin una subrutina, mientras que Quicksort podría tener al menos una subrutina. Esto muestra el patrón de que una mayor sofisticación del código a veces puede mejorar la complejidad asintótica a expensas de la simplicidad del código. a veces hay un cambio extremo similar al concepto de rendimientos marginales decrecientes (originario de la economía) donde grandes cantidades de complejidad de código [que requieren documentos completos llenos de thms y pruebas para justificar] solo compra mejoras muy pequeñas en la complejidad asintótica. esto se muestra como un ejemplo especialmente conmultiplicación de matrices e incluso se puede graficar .
fuente