¿Debería seleccionar siempre el algoritmo con el menor orden de complejidad?

Muy usualmente, pero no siempre. El análisis asintótico considera el peor de los casos y barre las constantes aditivas y multiplicativas debajo de la alfombra. Los algoritmos con un mejor tiempo de ejecución asintótico funcionarán mejor en entradas más grandes que un cierto tamaño, pero ese tamaño puede ser enorme, o las constantes ocultas en la notación big-O pueden ser absolutamente masivas.

Un ejemplo muy práctico: cuando se ordenan listas pequeñas (quizás unos cientos de elementos o más cortas), se sabe que ciertos algoritmos O (n ^ 2) se ejecutan más rápido que los hábiles algoritmos O (n log n) que son mucho mejores asintóticamente. (En estos tamaños de entrada más pequeños, las constantes realmente importan y la sobrecarga de recursión excesiva comienza a sumarse …) Muchos algoritmos de clasificación altamente optimizados para el rendimiento realmente cambian a la clasificación de burbujas (o algo así) después de haber dividido la entrada en subproblemas lo suficientemente pequeños.

También considere que un algoritmo muy inteligente con una complejidad asintótica asesina podría ser muy difícil de implementar. Este es el caso con varias estructuras de datos similares a las del montón más novedosas, como el montón de Fibonacci. (Consulte también las colas Brodal: se obtienen operaciones de almacenamiento dinámico O (1), pero se dice que la implementación es tan desagradable que incluso el inventor piensa que “no son aplicables en la práctica”). Este podría ser el caso para algunos modernos algoritmos de tabla hash también.

Y, en ocasiones, el tiempo de ejecución promedio puede ser una consideración más importante. Esto no es muy común, pero especialmente cuando eliges entre algoritmos que usan algún tipo de heurística, incluso si funcionan de manera idéntica en el peor de los casos, puede suceder que uno de ellos no se encuentre con el peor de los casos con tanta frecuencia. Creo que el algoritmo simplex para programación lineal es un buen ejemplo aquí: su tiempo de ejecución promedio es (algo inexplicablemente) bueno, lo que lo convierte en una opción popular en la práctica, pero su peor desempeño aún es bastante terrible.

Gracias por el A2A.

Depende. Se trata principalmente de compensaciones. ¿Qué pasa si otro algoritmo tiene una complejidad de tiempo promedio aceptable y es mucho más fácil escribir el código de manera limpia y correcta?

¿Estás escribiendo una biblioteca? Tal vez las necesidades de rendimiento sean mayores que la claridad del código (ya que sus clientes quieren resultados rápidos de su biblioteca, pero no necesitarán leer el código)

¿Estás escribiendo parte de una pieza de software que leerá el resto de tu equipo? Luego, busque un código más limpio con un rendimiento aceptable, ya que será necesario modificarlo y, posteriormente, ser propiedad de otra persona.

Por lo general, pero no necesariamente .

Depende de la escala del problema. Si el problema es demasiado pequeño para aplicar esos algoritmos que es difícil de implementar y comprender, ¿por qué no probar el más simple?

Incluso para algunos problemas, los algoritmos exponenciales tienen un mejor rendimiento que los polinomios.

Exactamente como los otros han mencionado. Menos complejidad no significa necesariamente que vaya a correr más rápido. Hay otros factores a considerar, como el tamaño de los datos, los requisitos de tiempo constante, etc.

Por ejemplo, piensa en un diccionario. Puede ser O (1), pero ¿qué pasa si el algoritmo de hash tarda demasiado? Entonces podría ser mejor usar un BST en lugar de trabajar en O (log N), o si el tamaño de la muestra es pequeño, una búsqueda secuencial normal podría ser mucho más rápida a pesar de que es O (N).

Como muestra, mire la biblioteca DotNet System.Collections. Hay varios diccionarios / tablas hash allí porque a veces obtendría un mejor rendimiento de una implementación que de otra.

Lo mejor que puedes hacer es probarlo. Use sus muestras de datos y tamaño esperados para ver cuál funciona mejor en su escenario particular. Obviamente, esto no se puede hacer para todos los casos, por lo que también se necesita algo de sentido común.

Editar: Aquí hay un ejemplo en el que un algoritmo O (N ^ 2) realmente clasifica basura / algoritmos hash: Encontrar el primer número único.

Sí, para una definición adecuada de “complejidad”. No, si por “complejidad” te refieres a “complejidad de tiempo asintótica” solamente.

En la vida real, la complejidad del tiempo restringe el conjunto de algoritmos que puede usar: algunos de ellos serán demasiado lentos para los tamaños de datos que tiene (o se estima que tendrán en el futuro). De los algoritmos que son lo suficientemente buenos , puede elegir uno usando cualquier criterio adicional que pueda aplicarse en su situación.

Ejemplos:

  • si escribe una biblioteca de uso general, puede ser una buena idea buscar el mejor rendimiento, ya que esto hace que la biblioteca sea utilizable en más casos en el futuro, incluso en aquellos que no conoce en este momento.
  • En la mayoría de los otros casos, debe optar por el algoritmo que es lo suficientemente bueno y el más sencillo de implementar . Los beneficios deben ser claros: estará listo rápidamente, hay una menor probabilidad de errores y el código será fácil de mantener en el futuro.

(Como nota al margen, esto es cierto incluso en concursos de programación algorítmica. El algoritmo más eficiente no siempre es la mejor solución, vale la pena si puede reconocer las situaciones en las que puede intercambiar cierta eficiencia para facilitar la implementación).