¿Es un problema ser un programador sin conocimiento sobre la complejidad computacional?


30

Me asignaron un ejercicio en mi universidad. Me lo llevé a casa e intenté programar un algoritmo para resolverlo, supongo que era algo relacionado con gráficos, encontrar componentes conectados.

Luego hice lo más trivial que se me ocurrió y luego se lo mostré a mi profesor. Después de una breve observación, percibió que la complejidad del tiempo de ejecución de mi solución era inviable y mostró algo más eficiente. Y existe una tradición de programadores que no tienen idea de qué es la complejidad computacional (yo era uno de esos), entonces, ¿es un problema si un programador no tiene idea de qué es la complejidad computacional?


3
Aviso para el moderador : por favor no use comentarios para discusiones extensas o para publicar respuestas concisa. Puede usar la sala de chat para discutir esta pregunta; comentarios anteriores han sido trasladados allí.
Gilles 'SO- deja de ser malvado'

44
Su título dice programador, pero su pregunta dice estudiante. En general, 'programador' implica 'programador profesional', entonces ¿se pregunta si es un problema ser un programador profesional sin conocimiento de la complejidad computacional? ¿O si está bien que un estudiante de programación no tenga ese conocimiento? Las dos son preguntas diferentes, incluso si resulta que tienen la misma respuesta.
corsiKa

Respuestas:


42

Sí, diría que saber algo sobre la complejidad computacional es imprescindible para cualquier programador serio. Mientras no esté lidiando con grandes conjuntos de datos, estará bien sin conocer la complejidad, pero si desea escribir un programa que aborde problemas graves, lo necesita.

En su caso específico, su ejemplo de encontrar componentes conectados podría haber funcionado para gráficos de hasta nodos. Sin embargo, si probaste un gráfico con nodos, entonces el algoritmo de tu profesor probablemente lo hubiera logrado en 1 segundo, mientras que tu algoritmo habría (dependiendo de cuán grave fuera la complejidad) tomado 1 hora, 1 día, o tal vez incluso 1 eternidad.100.000100100.000

Un error algo común que los estudiantes cometen en nuestro curso de algoritmos es iterar sobre una matriz como esta:

while array not empty
    examine first element of array
    remove first element from array

Puede que este no sea el código más hermoso, pero en un programa complicado, algo como esto podría aparecer sin que el programador lo sepa. Ahora, ¿cuál es el problema con este programa?

Supongamos que lo ejecutamos en un conjunto de datos de elementos. En comparación con el siguiente programa, el programa anterior se ejecutará más lento.50.000100.00050.000

while array not empty
    examine last element of array
    remove last element from array

Espero que esté de acuerdo en que tener el conocimiento para hacer que su programa se ejecute veces más rápido es probablemente algo importante para un programador. Comprender la diferencia entre los dos programas requiere algunos conocimientos básicos sobre la teoría de la complejidad y algunos conocimientos sobre los detalles del lenguaje en el que está programando.50.000

En mi lenguaje de pseudocódigo, "eliminar un elemento de una matriz" desplaza todos los elementos a la derecha del elemento que se está eliminando una posición desde la izquierda. Esto hace que eliminar el último elemento sea una operación ya que para hacerlo solo necesitamos interactuar con 1 elemento. Eliminar el primer elemento es ya que para eliminar el primer elemento, también debemos desplazar todos los demás elementos una posición hacia la izquierda.O ( n ) n - 1O(1)O(n)n1

Un ejercicio muy básico de complejidad es demostrar que el primer programa realizará operaciones mientras que el segundo programa usa solo operaciones. Si conectas , verás que un programa es drásticamente más eficiente que el otro.nn=100.00012n2nn=100.000

Este es solo un ejemplo de juguete, pero ya requiere una comprensión básica de la complejidad para distinguir la diferencia entre los dos programas, y si realmente está tratando de depurar / optimizar un programa más complicado que tiene este error, se necesita una comprensión aún mayor para encontrar fuera de donde está el error. Porque un error como eliminar un elemento de una matriz de esta manera puede ocultarse muy bien por abstracciones en el código.

Tener una buena comprensión de la complejidad también ayuda cuando se comparan dos enfoques para resolver un problema. Suponga que ha ideado dos enfoques diferentes para resolver el problema de los componentes conectados por su cuenta: para decidir entre ellos sería muy útil si pudiera (rápidamente) estimar su complejidad y elegir el mejor.


10
"So long as you are not dealing with huge data sets you will be fine not knowing complexity"Esto suele ser cierto, pero no siempre es así. Por ejemplo, un O(n!)algoritmo no será viable incluso para conjuntos de datos relativamente pequeños. Si usa un O(n!)algoritmo en el que podría haber usado O(n^2)su programa, tardará 36.288 veces más en ejecutarse en un tamaño de datos de 10 . Con un tamaño de datos de 20, está viendo 2.4 quintillones de operaciones.
reirab

1
Creo que el ejemplo de @ reirab debería incluirse en la respuesta. Es más dramático y demuestra su punto de manera más decisiva. Y personalmente he sido mordido por tales algoritmos, antes de aprender la complejidad computacional.
Siyuan Ren

2
Creo que hay un problema mayor en juego. Si simplemente no sabe, usted mismo selecciona las tareas donde esto no es necesario. Entonces, puede decir que casi todas las preguntas que necesito saber que X termina, podrían ser útiles. Así que, independientemente de si es crítico, es bueno saberlo o podría llegar a morderte al final.
joojaa

"Comprender la diferencia entre los dos programas requiere un conocimiento básico sobre la teoría de la complejidad". Creo que para este ejemplo en particular no lo hace. Puede perfilarlo, observar que todo el tiempo se toma en "eliminar elemento", saber (sin comprender la teoría de la complejidad) que eliminar el último elemento es más rápido que eliminar el primero, realizar el cambio y, por lo tanto, acelerar el programa. La ventaja de comprender la teoría de la complejidad es que le permite cuantificar libremente tales problemas sin perfilarlos, por lo que puede optimizar "prematuramente".
Steve Jessop

... y en general sospecho que todos o casi todos los ejemplos prácticos pueden resolverse, uno por uno, sin referencia a la teoría de la complejidad. En este caso, saber que copiar muchos datos es más lento que no hacerlo, no es una "teoría de la complejidad". Pero, por supuesto, sigue siendo útil en la programación (y en cualquier profesión) tener un buen modelo mental de principios que surgen comúnmente, porque puedes analizar, discutir y resolver tales problemas rutinariamente por principio en lugar de uno a la vez por medios ad hoc.
Steve Jessop

26

Esta es una refutación de la respuesta de Tom van der Zanden , que afirma que es imprescindible.

La cosa es que, la mayoría de las veces, 50,000 veces más lento no es relevante (a menos que trabajes en Google, por supuesto).

Si la operación que realiza toma un microsegundo o si su N nunca supera un cierto umbral (una gran parte de la codificación realizada hoy en día) NUNCA importará. En esos casos, pensar en la complejidad computacional solo le hará perder tiempo (y probablemente dinero).

La complejidad computacional es una herramienta para entender por qué algo puede ser lento o escalar mal, y cómo mejorarlo, pero la mayoría de las veces es una exageración total.

He sido programador profesional durante más de cinco años y nunca he encontrado la necesidad de pensar en la complejidad computacional al hacer un bucle dentro de un bucle O (M * N) porque siempre la operación es realmente rápida o M y N son tan pequeño.

Hay cosas mucho más importantes, de uso general y más difíciles de entender para cualquiera que realice trabajos de programación (el enhebrado y el perfilado son buenos ejemplos en el área de rendimiento).

Por supuesto, hay algunas cosas que nunca podrá hacer sin comprender la complejidad computacional (por ejemplo: encontrar anagramas en un diccionario), pero la mayoría de las veces no lo necesita.


3
Para ampliar su punto, hay casos en los que demasiado énfasis en la complejidad computacional puede llevarlo por mal camino. Por ejemplo, puede haber situaciones en las que el algoritmo "mejor" sea realmente más lento para entradas pequeñas. El perfilador es la última fuente de verdad.
Kevin Krumwiede

2
@Kevin Krumwiede, estoy completamente de acuerdo con usted en que optimizar una especie para un conjunto de datos trivial es excesivo. Pero también ilustra que tener al menos una comprensión de la complejidad sigue siendo importante. El entendimiento es lo que lo llevará a tomar la decisión de que un tipo de burbuja es apropiado en lugar de otro algoritmo más complejo.
Kent A.

44
Cuando sabe que el conjunto de datos es pequeño en todos los casos, puede salirse con la suya. Sin embargo, debe tener mucho cuidado con el exceso de complejidad en las cosas llamadas dentro de los bucles; no hace mucho, reduje un minuto el tiempo de ejecución a un segundo de esta manera. También he encontrado un problema O (n ^ 8) una vez (validación de datos). Mucha atención lo redujo a 12 horas.
Loren Pechtel

77
Nunca he encontrado la necesidad de pensar en la complejidad computacional al hacer un bucle dentro de un bucle O (M * N) porque siempre la operación es realmente rápida o M y N son muy pequeños. - Irónicamente, el argumento que presenta muestra que sí pensó en la complejidad computacional. Decidió que no es un problema relevante para lo que está haciendo y posiblemente con razón, pero aún es consciente de la existencia de este problema, y ​​si alguna vez plantea un problema, podría reaccionar ante él antes de que ocurran consecuencias graves en el nivel de usuario.
Wrzlprmft

44
La optimización prematura es la raíz de todo mal, pero la pesimismo prematura es la raíz de al menos una buena cantidad de usuarios molestos. Es posible que no necesite poder resolver una relación de recurrencia, pero si, como mínimo, no es capaz de distinguir entre O (1), O (N) y O (N ^ 2), especialmente cuando Estás anidando bucles, alguien tendrá que limpiar el desorden más tarde. Fuente: los líos que tuve que limpiar más tarde. Un factor 50,000 es tan grande que es mejor que sepas si aún puedes permitírtelo más tarde , cuando tus entradas hayan crecido.
Jeroen Mostert

14

He estado desarrollando software durante unos treinta años, trabajando tanto como contratista como empleado, y he tenido bastante éxito en ello. Mi primer idioma fue BASIC, pero enseguida aprendí el lenguaje de máquina para obtener una velocidad decente de mi caja de baja potencia. He pasado mucho tiempo en perfiladores a lo largo de los años y he aprendido mucho sobre la producción de código optimizado rápido y eficiente en memoria.

Independientemente de decir, soy autodidacta. Nunca encontré la notación O hasta que comencé a entrevistar hace unos años. Nunca apareció en mi trabajo profesional, EXCEPTO durante las entrevistas. Así que tuve que aprender los conceptos básicos solo para manejar esa pregunta en las entrevistas.

Me siento como el músico de jazz que no sabe leer partituras. Todavía puedo jugar bien. Sé acerca de las tablas hash (diablos, inventé las tablas hash antes de saber que ya se habían inventado) y otras estructuras de datos importantes, e incluso podría conocer algunos trucos que no enseñan en la escuela. Pero creo que la verdad es que si quieres tener éxito en esta profesión, tendrás que ir al cine independiente o aprender las respuestas a las preguntas que te harán durante las entrevistas.

Por cierto, recientemente me entrevisté para un rol de desarrollador web front-end. Me hicieron una pregunta donde la respuesta requería tanto el conocimiento de la complejidad computacional como los logaritmos. Logré recordar suficientes matemáticas de hace veinte años para responderlas más o menos correctamente, pero fue un poco desagradable. Nunca he tenido que usar logaritmos en ningún desarrollo front-end.

¡Buena suerte para ti!


2
Entonces, ¿tu respuesta es "sí"?
Raphael

66
TL; DR: "sí". Sin embargo, en mi experiencia, no va a hablar sobre la complejidad computacional en la mayoría de los trabajos después de ser contratado. Sí, conozca sus estructuras de datos y su rendimiento, pero solo saber que un algoritmo es O (n) o lo que no sea un buen programador. Es mucho mejor concentrarse en escribir un buen código rápidamente y luego optimizar los puntos calientes más adelante. La legibilidad y la facilidad de mantenimiento suelen ser más importantes para la mayoría del código que el rendimiento.
Scott Schafer

3
Creo que puede suceder que la complejidad surja en un entorno corporativo, pero la primera preocupación real para las empresas es el envío : si funciona, es lo suficientemente bueno, hasta que haya un presupuesto disponible para mejorar la aplicación, o un cliente regrese para quejarse por problemas actuaciones. En situaciones b2b para proyectos ad hoc, es probable que sea poco común. En b2c, o en mercados altamente competitivos (productos listos para usar), probablemente aparecería con más frecuencia, con el efecto directo de elevar la barra de entrada para nuevos empleados.
didierc

44
@didierc "Lo suficientemente bueno" también es lo que rompe las cosas todo el tiempo.
Raphael

1
@didierc 1) Bueno, las personas con antecedentes sólidos en CS tienen (con suerte) una buena intuición de lo que es correcto y lo que no, mientras que los solucionadores de problemas ad-hoc pueden cometer errores "simples". Asegurarse de que la ejecución después de compilaciones múltiples es exactamente lo que fue específico es altamente no trivial, y afaik un problema no resuelto. 2) no .
Raphael

9

La pregunta es bastante subjetiva, así que creo que la respuesta es que depende .

No importa mucho si trabaja con pequeñas cantidades de datos. En estos casos, generalmente está bien usar lo que sea, por ejemplo, la biblioteca estándar de su idioma.

Sin embargo, cuando maneja grandes cantidades de datos, o por alguna otra razón insiste en que su programa es rápido, debe comprender la complejidad computacional. Si no lo hace, ¿cómo sabe cómo se debe resolver un problema o qué tan rápido es posible resolverlo? Pero comprender la teoría justa no es suficiente para ser un buen programador. Creo que para producir código extremadamente rápido, también debe comprender cómo funciona su máquina (cachés, diseño de memoria, el conjunto de instrucciones) y qué hace su compilador (los compiladores hacen su mejor esfuerzo, pero no son perfectos).

En resumen, creo que comprender la complejidad claramente te convierte en un mejor programador.


1
Creo que generalmente tiene una idea correcta, pero "subjetivo" no describe este problema adecuadamente; "circunstancial" sería una mejor palabra. Además, uno puede escribir programas muy lentos que no operan con muchos datos. Recientemente respondí una pregunta en math.se sobre la representación / almacenamiento polinomial. Por lo general, esto implica una cantidad bastante pequeña de datos, por ejemplo, son típicos unos polinomios de 1000 términos; Sin embargo, existen enormes diferencias en el mundo real en el rendimiento (cientos o miles de segundos frente a unos segundos para una multiplicación) dependiendo de la implementación.
Fizz

4

Ciertamente es un problema si alguien que está desarrollando algoritmos significativos no comprende la complejidad del algoritmo. Los usuarios de un algoritmo generalmente dependen de una buena calidad de implementación que tiene buenas características de rendimiento. Si bien la complejidad no es el único contribuyente a las características de rendimiento de un algoritmo, es significativo. Alguien que no comprende la complejidad del algoritmo tiene menos probabilidades de desarrollar algoritmos con características de rendimiento útiles.

No es un problema para los usuarios de un algoritmo, suponiendo que los algoritmos disponibles sean de buena calidad. Esto es cierto para los desarrolladores que usan lenguajes que tienen una biblioteca estándar significativa, bien especificada, solo necesitan saber cómo elegir un algoritmo que satisfaga sus necesidades. El problema viene cuando hay múltiples algoritmos de algún tipo (por ejemplo, clasificación) disponibles dentro de una biblioteca, porque la complejidad es a menudo uno de los criterios para elegir. Un desarrollador que no comprende la complejidad no puede entender la base para elegir un algoritmo efectivo para su tarea en cuestión.

Luego están los desarrolladores que se centran (por falta de una mejor descripción) en preocupaciones no algorítmicas. Por ejemplo, pueden centrarse en desarrollar interfaces de usuario intuitivas. Dichos desarrolladores a menudo no tendrán que preocuparse por la complejidad del algoritmo, aunque, nuevamente, pueden confiar en que las bibliotecas u otro código se desarrollen con una alta calidad.


3

Depende, pero no de la cantidad de datos con los que está trabajando, sino del tipo de trabajo que realiza, los programas que desarrolla.

Llamemos al programador que no conoce la complejidad conceptual del programador novato.

El programador novato puede hacer:

  • desarrollar bases de datos de big data: no tiene que saber cómo funciona en el interior, todo lo que tiene que saber son las reglas sobre el desarrollo de bases de datos. Él sabe cosas como: qué debe indexarse, ... dónde es mejor hacer redundancia en los datos, donde no es ...
  • hacer juegos: solo tiene que estudiar cómo funciona un motor de juego y seguir sus paradigmas, los juegos y los gráficos de computadora son un problema de datos bastante grande. Considere 1920 * 1080 * 32bit = cca 7.9MB para una sola imagen / fotograma ... @ 60 FPS es al menos 475MB / s. Considere que solo una copia innecesaria de la imagen de pantalla completa desperdiciaría alrededor de 500 MB de rendimiento de memoria por segundo. Pero, no necesita preocuparse por eso, ¡porque solo usa el motor!

El programador novato no debe hacer:

  • desarrolle programas complejos de uso frecuente sin importar el tamaño de los datos con los que está trabajando, ... por ejemplo, los datos pequeños no causarán un impacto notable de una solución inadecuada durante el desarrollo, porque será más lento que el tiempo de compilación, etc. Entonces, 0.5 seg. para un programa simple no es mucho desde la perspectiva del programador novato. Bueno, considere el servidor servidor, que ejecuta este programa veinte veces por segundo. ¡Se necesitarían 10 núcleos para poder sostener esa carga!
  • Desarrollar programas para dispositivos integrados. Los dispositivos integrados funcionan con datos pequeños, pero deben ser tan eficientes como sea posible, ya que las operaciones redundantes generan un consumo innecesario de energía.

Entonces, el programador novato está bien, cuando solo quieres usar tecnologías. Entonces, cuando se trata del desarrollo de nuevas soluciones, tecnologías personalizadas, etc. Entonces es mejor contratar un programador no novato.

Sin embargo, si la compañía no desarrolla nuevas tecnologías, solo usa las ya hechas. Sería una pérdida de talento contratar a programadores calificados y talentosos. Lo mismo se aplica, si no desea trabajar en nuevas tecnologías y está bien poner las ideas de los clientes en diseños y programas utilizando marcos ya creados, entonces es una pérdida de tiempo aprender algo que nunca necesitará, excepto si es tu hobby y te gustan los desafíos lógicos.


1
Esta respuesta podría mejorarse si usara una etiqueta más neutral, o ninguna etiqueta, como la otra respuesta que usó el término "programador incompetente".
Moby Disk

1
No estoy seguro de lo que quiere decir con "complejidad conceptual". Mi experiencia es que las personas que no saben lo suficiente sobre árboles o tablas hash no pueden tomar decisiones inteligentes sobre cómo indexar (partes de) una gran base de datos.
Fizz

3

Dudo un poco en escribir una respuesta aquí, pero como me encontré molestando a varios otros '[algunos de mis comentarios fueron trasladados al chat], así es como lo veo ...

Hay muchos niveles / grados de conocimiento en muchas cosas en informática (y con este término me refiero aproximadamente a la unión de la informática con la tecnología de la información). La complejidad de la computación seguramente es un campo vasto (¿Sabes qué es OptP? ¿O qué dice el teorema de Abiteboul-Vianu?) Y también admite mucha profundidad: la mayoría de las personas con un título de CS no pueden producir las pruebas de expertos que se dedican a la investigación publicaciones en complejidad computacional.

n2

Sinceramente, me atrevería a comparar la situación de saber cuándo aplicar conceptos de complejidad computacional (y saber cuándo puede ignorarlos de manera segura) con la práctica algo común (fuera del mundo de Java) de implementar algún código sensible al rendimiento en C y el insensible al rendimiento cosas en Python, etc. (Como comentario aparte, esto se llamó en una charla de Julia el "compromiso estándar" ). Saber cuándo no tiene que pensar en el rendimiento le ahorra tiempo de programación, que también es un bien bastante valioso.

Y un punto más es que conocer la complejidad computacional no lo hará automáticamente bueno para optimizar programas; necesita comprender más cosas relacionadas con la arquitectura, como la ubicación de caché, [a veces] canalización, y hoy en día también programación paralela / multi-core; este último tiene tanto su propia teoría de la complejidad como también consideraciones prácticas; una muestra de este último de un documento SOSP de 2013 "Cada esquema de bloqueo tiene sus quince minutos de fama. Ninguno de los nueve esquemas de bloqueo que consideramos supera constantemente a cualquier otro, en todas las arquitecturas o cargas de trabajo de destino. Estrictamente hablando, para buscar la óptima, un por lo tanto, el algoritmo de bloqueo debe seleccionarse en función de la plataforma de hardware y la carga de trabajo esperada ".


1
A la larga, desarrollar o encontrar un algoritmo mejor suele ser más beneficioso que cambiar el lenguaje de programación para los bits sensibles al rendimiento. Estoy de acuerdo con usted en que existe una fuerte asociación entre la falta de comprensión de la complejidad y la optimización prematura, porque generalmente apuntan a los bits menos sensibles al rendimiento para la optimización.
Rob

1
En la práctica, (inadvertido) los algoritmos de Schlemiel el pintor son mucho más frecuentes que la clasificación O (n ^ 2).
Peter Mortensen

-1

Si no sabes big-O, debes aprenderlo. No es difícil y es realmente útil. Comience con la búsqueda y clasificación.

Noto que muchas respuestas y comentarios recomiendan la creación de perfiles , y casi siempre significan usar una herramienta de creación de perfiles .

El problema es que las herramientas de creación de perfiles están en todo el mapa en términos de cuán efectivas son para encontrar lo que necesita para acelerar. Aquí he enumerado y explicado los conceptos erróneos que sufren los perfiladores.

El resultado es que los programas, si son más grandes que un ejercicio académico, pueden contener gigantes dormidos , que incluso el mejor perfilador automático no puede exponer. Esta publicación muestra algunos ejemplos de cómo los problemas de rendimiento pueden ocultarse de los perfiladores.

Pero no pueden esconderse de esta técnica.


Usted afirma que "Big-Oh" es útil, pero luego defiende un enfoque diferente. Además, no veo cómo aprender "Big-Oh" (matemáticas) puede "comenzar con la búsqueda y la clasificación" (problemas de algoritmos).
Raphael

@Raphael: No defiendo un enfoque diferente: es ortogonal. Big-O es un conocimiento básico para comprender algoritmos, mientras que encontrar problemas de rendimiento en software que no es de juguete es algo que se hace después de que el código se escribe y ejecuta, no antes. (A veces, los académicos no saben esto, por lo que continúan enseñando gprof, haciendo más daño que bien.) Al hacerlo, puede o no descubrir que el problema es el uso de un algoritmo O (n * n), por lo que debe ser capaz de reconocer eso (Y big-O es solo una propiedad matemáticamente definida de algoritmos, no un tema diferente.)
Mike Dunlavey

"Y big-O es solo una propiedad matemáticamente definida de los algoritmos, no un tema diferente". - Eso está mal, y peligrosamente. "Big-Oh" define clases de funciones ; per se, no tiene nada que ver con algoritmos en absoluto.
Raphael

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.