Gran O Pregunta sobre un algoritmo con (n ^ 2 + n) / 2 tasa de crecimiento

16

Estoy haciendo esta pregunta porque estoy confundido acerca de un aspecto con respecto a la notación O grande.

Estoy usando el libro, Estructuras de datos y abstracciones con Java de Frank Carrano. En el capítulo sobre "Eficiencia de los algoritmos", muestra el siguiente algoritmo:

int sum = 0, i = 1, j = 1
for (i = 1 to n) {
    for (j = 1 to i)
        sum = sum + 1
}

Inicialmente describe este algoritmo como teniendo una tasa de crecimiento de (n 2  + n) / 2 . Que mirarlo parece intuitivo.

Sin embargo, se afirma que (n 2  + n) / 2 se comporta como n 2 cuando n es grande. En el mismo párrafo se afirma (n 2  + n) / 2 también se comporta como n 2 / 2 . Él usa esto para clasificar el algoritmo anterior como O (n 2 ) .

Entiendo que (n 2  + n) / 2 es similar a la n 2 / 2 , porque porcentualmente, n hay mucha diferencia. Lo que no entiendo es por qué (n 2  + n) / 2 y N 2 son similares, cuando n es grande.

Por ejemplo, si n = 1,000,000 :

(n^2 + n) / 2 =  500000500000 (5.000005e+11)
(n^2) / 2     =  500000000000 (5e+11)
(n^2)         = 1000000000000 (1e+12)

Ese último no es similar en absoluto. De hecho, obviamente, es el doble que el del medio. Entonces, ¿cómo puede Frank Carrano decir que son similares? Además, cómo se clasifica el algoritmo como O (n 2 ) . Mirando ese circuito interno, diría que fue n 2 + n / 2

Andrew S
fuente
Si está interesado yo le había dado una respuesta para tres bucles anidados con árbol de ejecución de verificación diagrama Un rompecabezas relacionados con bucles anidados
Grijesh Chauhan
1
Básicamente, la idea es que a medida que ncrece, tanto las funciones 'n ^ 2' como su función, se comportan de manera similar, hay una constante diferencia en su tasa de crecimiento. Si tiene una expresión compleja, la función que crece más rápido domina.
AK_
1
@MichaelT: No creo que esto sea un duplicado de esa pregunta, ya que la otra es simplemente una cuestión de conteo erróneo. Esta es una pregunta más sutil sobre por qué se ignoran los términos menores (específicamente, multiplicadores constantes y polinomios de menor grado). Aparentemente, el interrogador aquí ya comprende la cuestión planteada en la otra pregunta, y una respuesta que sea suficiente para esa pregunta no responderá a esta.
sdenham

Respuestas:

38

Al calcular la complejidad Big-O de un algoritmo, lo que se muestra es el factor que da la mayor contribución al aumento en el tiempo de ejecución si aumenta el número de elementos sobre los que ejecuta el algoritmo.

Si tiene un algoritmo con una complejidad de (n^2 + n)/2y duplica el número de elementos, entonces la constante 2no afecta el aumento en el tiempo de ejecución, el término nprovoca una duplicación en el tiempo de ejecución y el término n^2provoca un aumento de cuatro veces en la ejecución hora.
Como el n^2término tiene la mayor contribución, la complejidad de Big-O es O(n^2).

Bart van Ingen Schenau
fuente
2
Me gusta eso, se está volviendo un poco más claro.
Andrew S
77
Esto es muy ondulado a mano. Podría ser cierto o podría ser falso. Si puede tomar una pequeña cantidad de matemáticas, vea una de las respuestas a continuación.
usr
2
Este razonamiento es demasiado vago: significaría que podríamos concluir eso O(n * log n) = O(n), lo cual no es cierto.
cfh
Puede que no sea la respuesta más precisa o la más semánticamente correcta, pero lo importante aquí es que me llevó a comenzar a comprender el punto central y creo que ese era el objetivo del autor. Es deliberadamente vago, ya que los detalles a menudo pueden distraer de los principios básicos. Es importante ver la madera de los árboles.
Andrew S
Bart realmente estaba hablando de términos, no de factores. Entendiendo eso, no podemos concluir eso O(n * log n) = O(n). Creo que esto da una buena explicación de la lógica detrás de la definición.
Mark Foskey
10

La definición es que

f(n) = O(g(n))

si existe alguna constante C> 0 tal que, para todas las n mayores que algunas n_0, tenemos

|f(n)| <= C * |g(n)|

Esto es claramente cierto para f (n) = n ^ 2 y g (n) = 1/2 n ^ 2, donde la constante C debería ser 2. También es fácil ver que es cierto para f (n) = n ^ 2 yg (n) = 1/2 (n ^ 2 + n).

cfh
fuente
44
"Si existe alguna constante C> 0 tal que, para todo n," debería ser "Si hay algunas constantes C, n_0 tal que, para todo n> n_0,"
Taemyr
@Taemyr: siempre y cuando la función gno sea cero, en realidad no es necesaria, ya que siempre puede aumentar la constante C para hacer que la declaración sea verdadera para los muchos primeros valores n_0.
cfh
No, siempre que estemos viendo funciones, no hay un número finito de valores n_0 potenciales.
Taemyr
@Taemyr: n_0 es un número finito. Elija C = max {f (i) / g (i): i = 1, ..., n_0}, y luego la declaración siempre se mantendrá para los primeros valores de n_0, como puede verificar fácilmente.
cfh
En CS esto es menos preocupante porque n suele ser el tamaño de entrada, y por lo tanto discreto. En cuyo caso, uno puede elegir C de modo que n_0 = 1 funcione. Pero la definición formal es n mayor que un umbral, lo que elimina muchas dudas al aplicar la definición.
Taemyr
6

Cuando se habla de complejidad, solo le interesan los cambios en el factor tiempo basados ​​en el número de elementos ( n).

Como tal, puede eliminar cualquier factor constante (como el 2aquí).

Esto te deja con O(n^2 + n).

Ahora, para un producto razonablemente grande n, es decir n * n, será significativamente más grande que solo n, que es la razón por la que también puedes omitir esa parte, lo que te deja con una complejidad final O(n^2).

Es cierto, para números pequeños habrá una diferencia significativa, pero esto se vuelve más marginal cuanto más grande se nvuelve.

Mario
fuente
¿Qué tan grande tiene que ser n para que la diferencia se vuelva marginal? Además, ¿por qué se elimina el / 2, su existencia reduce a la mitad el valor?
Andrew S
66
@AndrewS Porque Big O Notation habla de crecimiento. Dividir por 2 es irrelevante fuera del contexto de los puntos de referencia y las marcas de tiempo porque, en última instancia, no cambia la tasa de crecimiento. Sin embargo, el componente más grande lo hace, y eso es todo lo que conserva.
Neil
2
@Niel, brillante, muy claro. Desearía que los libros lo pusieran así. A veces pienso que los autores saben demasiado que olvidan que los simples mortales no poseen su conocimiento funcional y, por lo tanto, no aclaran puntos importantes, sino que lo entierran en alguna descripción matemática formal o lo omiten todo creyendo que está implícito.
Andrew S
¡Ojalá pudiera votar esta respuesta más de una vez! @Neil, deberías estar escribiendo los libros Big O.
Tersosauros
3

No es que "(n² + n) / 2 se comporte como n² cuando n es grande", es que (n² + n) / 2 crece como n² a medida que n aumenta .

Por ejemplo, a medida que n aumenta de 1,000 a 1,000,000

(n² + n) / 2  increases from  500500 to  500000500000
(n²) / 2      increases from  500000 to  500000000000
(n²)          increases from 1000000 to 1000000000000

Del mismo modo, a medida que n aumenta de 1,000,000 a 1,000,000,000

(n² + n) / 2  increases from  500000500000 to  500000000500000000
(n²) / 2      increases from  500000000000 to  500000000000000000
(n²)          increases from 1000000000000 to 1000000000000000000

Crecen de manera similar, de eso se trata Big O Notation.

Si trazas (n² + n) / 2 y n² / 2 en Wolfram Alpha , son tan similares que son difíciles de distinguir por n = 100. Si traza los tres en Wolfram Alpha , verá dos líneas separadas por un factor constante de 2.

ShadSterling
fuente
Esto es bueno, me lo deja muy claro. Gracias por responder.
Andrew S
2

Parece que necesitas resolver un poco más la notación O grande . Cuán conveniente es esta notación, es muy engañosa debido al uso de un signo igual, que no se usa aquí para denotar la igualdad de funciones.

Como saben, esta notación expresa una comparación asintótica de funciones, y escribir f = O (g) significa que f (n) crece como máximo tan rápido como g (n) cuando n llega al infinito. Una manera simple de traducir esto es decir que la función f / g está acotada. Pero, por supuesto, tenemos que ocuparnos de los lugares donde g es cero y terminamos con la definición más sólida que se puede leer en casi todas partes .

Esta notación resulta muy conveniente para la informática, es por eso que está tan extendida, pero debe manejarse con cuidado ya que el signo igual que vemos allí no denota una igualdad de funciones . Esto es más o menos como decir que 2 = 5 mod 3 no implica que 2 = 5 y si estás interesado en álgebra, puedes entender la gran notación O como un módulo de igualdad algo.

Ahora, para volver a su pregunta específica, es totalmente inútil calcular algunos valores numéricos y compararlos: por grande que sea un millón, no tiene en cuenta el comportamiento asintótico. Sería más útil trazar la relación de las funciones f (n) = n (n-1) / 2 y g (n) = n² , pero en este caso especial podemos ver fácilmente que f (n) / g (n) es menor que 1/2 si n> 0, lo que implica que f = O (g) .

Para mejorar su comprensión de la notación, debe

  • Trabaje con una definición limpia, no con una impresión borrosa basada en cosas similares , tal como lo acaba de experimentar, una impresión borrosa no funciona bien.

  • Tómese un tiempo para resolver ejemplos en detalle. Si trabaja tan solo cinco ejemplos en una semana, será suficiente para mejorar su confianza. Este es un esfuerzo que definitivamente vale la pena.


Nota algebraica secundaria Si A es el álgebra de todas las funciones Ν → Ν y C, el subalgebra de las funciones limitadas, dada una función f, el conjunto de funciones que pertenecen a O (f) es un submódulo C de A , y las reglas de cálculo en el gran La notación O simplemente describe cómo A opera en estos submódulos. Por lo tanto, la igualdad que vemos es una igualdad de submódulos C de A , este es solo otro tipo de módulo.

Michael Le Barbier Grünewald
fuente
1
Ese artículo de Wikipedia es difícil de seguir después de la primera pieza pequeña. Fue escrito para matemáticos consumados por matemático consumado y no es el tipo de texto introductorio que esperaría de un artículo enciclopédico. Gracias por su comprensión, aunque todo está bien.
Andrew S
¡Sobreestimas el nivel en el texto de Wikipedia! :) No está tan bien escrito, seguro. Graham, Knuth y Patashnik escribieron un libro encantador "Matemáticas concretas" para estudiantes de CS. También puede probar "el arte de la programación de computadoras" o un libro de teoría de números escrito en los años 50 (Hardy & Wright, Rose) ya que generalmente apuntan al nivel de estudiantes de secundaria. No necesita leer el libro completo, si elige uno, ¡solo la parte sobre asintótica! Pero antes de que necesite decidir cuánto necesita comprender. :)
Michael Le Barbier Grünewald
1

Creo que no entiendes lo que significa la gran notación O.

Cuando vea O (N ^ 2) básicamente significa: cuando el problema se vuelve 10 veces mayor, el tiempo para resolverlo será: 10 ^ 2 = 100 veces mayor.

Pongamos 1000 y 10000 en tu ecuación: 1000: (1000 ^ 2 + 1000) / 2 = 500500 10000: (10000 ^ 2 + 10000) / 2 = 50005000

50005000/500500 = 99,91

Entonces, mientras que la N era 10 veces más grande, las soluciones eran 100 veces más grandes. Por lo tanto se comporta: O (N ^ 2)

Pieter B
fuente
1

si n fuera un 1,000,000entonces

(n^2 + n) / 2  =  500000500000  (5.00001E+11)
(n^2) / 2      =  500000000000  (5E+11)
(n^2)          = 1000000000000  (1E+12)

1000000000000.00 ¿qué?

Si bien la complejidad nos brinda una forma de predecir un costo en el mundo real (segundos o bytes dependiendo de si estamos hablando de la complejidad del tiempo o la complejidad del espacio), no nos da una cantidad de segundos ni ninguna otra unidad en particular.

Nos da un grado de proporción.

Si un algoritmo tiene que hacer algo n² veces, entonces tomará n² × c para un valor de c que es el tiempo que toma cada iteración.

Si un algoritmo tiene que hacer algo n² ÷ 2 veces, tomará n² × c para un valor de c que sea el doble del tiempo que toma cada iteración.

De cualquier manera, el tiempo empleado sigue siendo proporcional a n².

Ahora, estos factores constantes no son algo que podamos ignorar; de hecho, puede tener el caso en el que un algoritmo con complejidad O (n²) funciona mejor que uno con complejidad O (n), porque si estamos trabajando en un pequeño número de elementos, el impacto de los factores consistentes es mayor y puede abrumar otras preocupaciones . (De hecho, incluso O (n!) Es lo mismo que O (1) para valores suficientemente bajos de n).

Pero no son de lo que nos dice la complejidad.

En la práctica, hay algunas maneras diferentes en que podemos mejorar el rendimiento de un algoritmo:

  1. Mejore la eficiencia de cada iteración: O (n²) todavía se ejecuta en n² × c segundos, pero c es más pequeño.
  2. Reduzca el número de casos vistos: O (n²) todavía se ejecuta en n² × c segundos, pero n es más pequeño.
  3. Reemplace el algoritmo con uno que tenga los mismos resultados, pero de menor complejidad: por ejemplo, si pudiéramos volver a convertir algo O (n²) en algo O (n log n) y, por lo tanto, cambiar de n² × c₀ segundos a (n log n) × c₁ segundos .

O, para verlo de otra manera, tenemos f(n)×csegundos tomados y puede mejorar el rendimiento reduciendo c, reduciendo no reduciendo lo que fdevuelve un determinado n.

Lo primero que podemos hacer mediante algunas microopciones dentro de un bucle, o usando un mejor hardware. Siempre dará una mejora.

Lo segundo que podemos hacer es quizás identificando un caso en el que podamos cortocircuitar el algoritmo antes de que todo sea examinado, o filtrar algunos datos que no serán significativos. No dará una mejora si el costo de hacerlo supera la ganancia, pero generalmente será una mejora mayor que el primer caso, especialmente con una n grande.

El tercero lo podemos hacer usando un algoritmo completamente diferente. Un ejemplo clásico sería reemplazar una ordenación de burbujas con una clasificación rápida. Con un número bajo de elementos, podríamos haber empeorado las cosas (si c₁ es mayor que c₀), pero generalmente permite las mayores ganancias, especialmente con n muy grande.

En el uso práctico, las medidas de complejidad nos permiten razonar sobre las diferencias entre los algoritmos precisamente porque ignoran la cuestión de cómo ayudaría reducir nc, para concentrarnos en examinar f()

Jon Hanna
fuente
"O (n!) Es lo mismo que O (1) para valores suficientemente bajos de n" es simplemente incorrecto. Tiene que haber una mejor manera de explicar que "cuando nse mantiene lo suficientemente bajo, Big-O no importa".
Ben Voigt
@BenVoigt Todavía no he encontrado uno con el mismo impacto retórico que tenía cuando lo leí por primera vez; originalmente no es mío, se lo robé a Eric Lippert, quien puede haberlo originado o haberlo tomado de otra persona. Por supuesto, hace referencia a bromas como "π es igual a 3 para valores pequeños de π y valores grandes de 3", que aún es más antiguo.
Jon Hanna
0

Factor constante

El punto de la notación O grande es que puede elegir un factor constante arbitrariamente grande para que O (función (n)) sea siempre mayor que la función C * (n). Si el algoritmo A es mil millones de veces más lento que el algoritmo B, entonces tienen la misma complejidad O, siempre que esa diferencia no crezca cuando n crece arbitrariamente grande.

Supongamos un factor constante de 1000000 para ilustrar el concepto: es un millón de veces más grande que lo necesario, pero eso ilustra el punto de que se consideran irrelevantes.

(n ^ 2 + n) / 2 "cabe dentro" O (n ^ 2) porque para cualquier n, no importa cuán grande sea, (n ^ 2 + n) / 2 <1000000 * n ^ 2.

(n ^ 2 + n) / 2 "no se ajusta" a un conjunto más pequeño, por ejemplo, O (n) porque para algunos valores (n ^ 2 + n) / 2> 1000000 * n.

Los factores constantes pueden ser arbitrariamente grandes: un algoritmo con un tiempo de ejecución de n años tiene una complejidad O (n) que es "mejor" que un algoritmo con un tiempo de ejecución de n * log (n) microsegundos.

Pedro es
fuente
0

Big-O se trata de "cuán complicado" es un algoritmo. Si tiene dos algoritmos, y uno tarda unos n^2*ksegundos en ejecutarse, y el otro tarda unos n^2*jsegundos en ejecutarse, entonces puede discutir cuál es mejor, y podría hacer algunas optimizaciones interesantes para tratar de afectar ko j, pero ambas estos algoritmos son muy lentos en comparación con un algoritmo que se necesita n*mpara ejecutar No importa cuán pequeñas sean las constantes ko j, para una entrada lo suficientemente grande, el n*malgoritmo siempre ganará, incluso si mes bastante grande.

Entonces llamamos a los dos primeros algoritmos O(n^2), y llamamos al segundo O(n). Divide muy bien el mundo en clases de algoritmos. De esto se trata big-O. Es como dividir los vehículos en automóviles, camiones y autobuses, etc. Hay muchas variaciones entre los automóviles, y puede pasar todo el día discutiendo si un Prius es mejor que un Chevy Volt, pero al final del día si necesita poner a 12 personas en una, entonces este es un argumento bastante insensato. :)

Jason Walton
fuente