2010-07-21 8 views
15

Me encontré con una afirmación de que HashSet <T> .Contains() es una operación O (1). Esto me sorprendió ya que cada discusión de hashing que he encontrado menciona la posibilidad de colisiones, lo que potencialmente puede llevar a O (n) tiempo de ejecución.O (1) hash ups?

Siendo curioso, miré en la documentación para HashSet <T> .Contiene y también HashTable.Contains. La documentación de ambos métodos hace el mismo reclamo.

Cuando miro en el reflector, HashSet <T> .Contains() se implementa con un bucle for, pasando por una lista de ranuras que contienen valores que tienen el mismo hash.

Ahora bien, esas mismas discusiones de hashing también han mencionado que un buen algoritmo hashing evita colisiones y en esas circunstancias la búsqueda será de hecho O (1). Pero mi comprensión de la notación Big O es que es el peor tiempo de ejecución, no el mejor.

¿El reclamo O (1) es incorrecto? ¿O me estoy perdiendo algo?

+2

Odio la notación O grande =] – Luiscencio

+2

@Luiscencio La notación Big O es simplemente las palabras que le permiten decirle a otro programador cómo se escalará una función. ¿Qué palabras sugiere que rápidamente le den a otro programador una idea semi-precisa de qué tan bien se escala una función determinada? –

+2

[broma] ¿qué pasa con sus "funciones es f ***** g comiendo el procesador f ***** g" – Luiscencio

Respuesta

9

Pero mi comprensión de la notación Big O es que es el peor tiempo de ejecución, no el mejor.

Lamentablemente, no existe un "estándar" para Big-O al describir algoritmos. A menudo, se usa para describir el caso general o promedio, no el peor de los casos.

De Wikipedia:

... esta notación es ahora con frecuencia también se utiliza en el análisis de algoritmos para describir el uso de un algoritmo de cálculo de los recursos: el peor de los casos, o caso promedio ...

En este caso, está describiendo un caso estándar, dado el hashing correcto. Si tiene hash adecuado en su lugar, el comportamiento limitante será constante para el tamaño N, por lo tanto O (1).

+4

Sí. Otro ejemplo destacado es el peor caso de Quicksort - O (n^2), pero a menudo se lo considera O (n log n) ya que es la complejidad promedio. – kennytm

+0

Cuando lo aprendí, se usa una O grande para indicar el límite, sin tener en cuenta el mejor/peor/caso promedio; sin embargo, en momentos en que los mejores, peores y promedio casos tienen una desconexión significativa, la O grande se usa típicamente para el análisis promedio de casos. Usa theta grande para el peor de los casos. –

+0

Eso es sorprendente, hubiera esperado que el peor de los casos fuera el uso más típico, sin embargo (especialmente para hash) tener el peor caso a menudo sería una motivación para buscar un mejor algoritmo. Sin embargo, puedo ver dónde sería útil el caso general/promedio. En el caso de hash, esperaría O (1) la mayor parte del tiempo. – ThatBlairGuy

7

En general, es O (1).

+0

Incluso considerando el bajo rendimiento conocido del incorporado 'GetHashCode'? No dependería de que sea O (1) ... –

+2

@Stephen: ¿De qué estás hablando? Además, incluso si 'GetHashCode' tarda una hora en regresar, sigue siendo O (1) - el rendimiento de' GetHashCode' no se escala con el tamaño del conjunto. – SLaks

+0

@SLaks, supongo que Stephen se estaba refiriendo a la poca adecuación de la implementación predeterminada para hash. Ver http://stackoverflow.com/questions/720177/default-implementation-for-object-gethashcode/720196#720196 –

5

No, Big O no define "el peor de los casos", sino que define un límite. Las búsquedas basadas en hash (con buenos algoritmos hash que proporcionan una distribución de valor eficiente y una baja tasa de colisión) progresan hacia un valor constante a medida que aumenta el número de elementos (nunca alcanzarán ese valor constante, pero ese es el límite.)

2

Creo que significa O (1) en promedio.

0

Mi comprensión de Big Oh es que el "peor caso" generalmente se refiere a la cantidad de elementos involucrados. Entonces, si una función fuera a realizar O (n) con 10 elementos, pero O (n al cuadrado) con 100 o más (no estoy seguro de que tal algoritmo realmente exista), entonces el algoritmo se considera O (n al cuadrado).

0

O (1) no significa necesariamente "el peor de los casos". Para los hashes, uno generalmente dice que el tiempo de búsqueda "esperado" es O (1), ya que la probabilidad de colisiones hash es pequeña.

+0

Eso es lo que me sorprendió: el fraseo en los diversos lugares donde encontré referencias a la búsqueda no decía "esperado" o "típico". Dijeron "es", lo que implica siempre. – ThatBlairGuy

6

Para una tabla hash correctamente implementada, las búsquedas tienen amortized complejidad de tiempo constante.

En la práctica, una sola consulta puede ser O (n) en caso de colisión, como dices. Sin embargo, si realiza una gran cantidad de búsquedas, la complejidad de tiempo promedio por operación es constante.

Wikipedia postular:

análisis amortizado se diferencia de rendimiento en el caso promedio en que la probabilidad no está involucrado; el análisis amortizado garantiza el tiempo por operación sobre el peor de los casos.

El método requiere el conocimiento de qué serie de operaciones son posibles. Este suele ser el caso con las estructuras de datos, que tienen un estado que persiste entre las operaciones. La idea básica es que una operación en el peor de los casos puede alterar el estado de tal manera que el peor de los casos no puede volver a ocurrir durante mucho tiempo, lo que "amortiza" su costo.

+1

+1, finalmente el importantísimo término "amortizado". –

+0

De hecho, la complejidad amortizada debe mencionarse en una buena descripción de la complejidad de la tabla hash. Pero tenga en cuenta que la complejidad O (1) amortizada requiere la suposición de que las claves están distribuidas de forma suficientemente aleatoria. Si un atacante elige las teclas para agregar al hash, puede forzar una colisión cada vez. Esto podría evitarse utilizando un hash criptográfico, pero estos son muy caros, por lo que obtendría un tiempo constante con una constante prohibitivamente grande. Otra forma es incluir una semilla aleatoria en el hash (perl hizo esto en algún punto). – Gilles

1

No, la notación Big-O no está necesariamente restringida al peor de los casos. En general, verá publicado Big-O para el mejor de los casos, el promedio de casos y el peor de los casos. Es solo que la mayoría de la gente tiende a enfocarse en el peor de los casos. Excepto en el caso de una tabla hash, el peor de los casos rara vez ocurre, por lo que usar el caso promedio tiende a ser más útil.

Sí, una buena función de reducción reduce la probabilidad de una colisión. Una mala función de hash puede causar el efecto de agrupamiento (donde los valores de hash diferentes al mismo valor exacto o cerca del mismo valor). Es fácil demostrar que HashSet puede convertirse en O (n) implementando la función GetHashCode de tal manera que devuelve el mismo valor todo el tiempo.

En un nutshull, sí HashSet y Dictionary se puede describir como que tiene O (1) la complejidad en tiempo de ejecución debido a que el énfasis está en el escenario de caso medio.

Por cierto, Big-O también se puede utilizar para analizar la complejidad amortizada. La complejidad amortizada es cómo se comporta una secuencia de operaciones separadas (ya veces incluso diferentes) cuando se agrupan juntas como si fueran una gran operación. Por ejemplo, se dice que un árbol splay ha amortizado O (log (n)) búsqueda, inserción y eliminación de complejidad incluso si el peor caso para cada uno podría O (n) y el mejor de los casos es O (1).

0

Las tablas hash no solo tienen un rendimiento de caja promedio O (1), pero si la función hash es aleatoria, para cualquier porcentaje dado P < 100%, el rendimiento que puede obtenerse P% de las veces el cuento de hadas diseñado es O (1). Aunque los casos parasitarios extremos se vuelven cada vez más severos a medida que aumenta el N, eso se equilibra con el hecho de que incluso los casos moderadamente parasitarios se vuelven cada vez menos probables.

Cuestiones relacionadas