2010-07-21 17 views
15

Ich lief über eine Behauptung, dass HashSet <T> .Contains() ist eine O (1) -Operation. Das hat mich überrascht, da jede Diskussion über Hashing die Möglichkeit von Kollisionen erwähnt, die möglicherweise zu O (n) Laufzeit führen.O (1) Hash-Lookups?

Neugierig, ich schaute in Dokumentation für HashSet <T>. Enthält und auch HashTable.Contains. Die Dokumentation für beide Methoden macht denselben Anspruch.

Wenn ich in Reflektor schaue, HashSet <T> .Contains() ist mit einer for-Schleife implementiert, durchläuft eine Liste von Steckplätzen mit Werten, die den gleichen Hash haben.

Nun haben die gleichen Diskussionen über Hashing auch erwähnt, dass ein guter Hashing-Algorithmus Kollisionen vermeidet und unter diesen Umständen wird die Suche in der Tat O (1) sein. Aber mein Verständnis der Big-O-Notation ist, dass es die schlechteste Laufzeit ist, nicht die beste.

Also ist die O (1) Behauptung falsch? Oder fehlt mir etwas?

+2

Ich hasse große O-Notation =] – Luiscencio

+2

@Luiscencio Große O-Notation ist einfach die Wörter, die Sie einem anderen Programmierer sagen können, wie eine Funktion skaliert wird. Welche Wörter schlagen Sie vor, dass ein anderer Programmierer schnell eine halb genaue Vorstellung davon bekommt, wie gut eine gegebene Funktion skaliert? –

+2

[Witz] Was ist mit Ihren "Funktionen ist f ***** g essen die f ***** g Prozessor" – Luiscencio

Antwort

9

Aber mein Verständnis der Big O-Notation ist, dass es die schlechteste Laufzeit ist, nicht die beste.

Leider gibt es keinen "Standard" für Big-O bei der Beschreibung von Algorithmen. Oft wird es verwendet, um den allgemeinen oder durchschnittlichen Fall zu beschreiben - nicht den schlimmsten Fall.

Von Wikipedia:

... diese Notation jetzt häufig auch bei der Analyse von Algorithmen verwendet einen Algorithmus, der Nutzung von Rechenressourcen zu beschreiben: den schlimmsten Fall oder durchschnittlichen Fall ...

In diesem Fall beschreibt es einen Standardfall, bei richtigem Hashing. Wenn Sie ein richtiges Hashing haben, wird das Begrenzungsverhalten für die Größe N konstant sein, also O (1).

+4

Ja. Ein anderes prominentes Beispiel ist Quicksort - O (n^2) worst case, wird aber oft als O (n log n) betrachtet, da dies die durchschnittliche Komplexität ist. – kennytm

+0

Wenn ich es gelernt habe, wird großes O verwendet, um das Limit zu bezeichnen, ohne Rücksicht auf den besten/schlechtesten/durchschnittlichen Fall; In Zeiten, in denen der beste, der schlechteste und der durchschnittliche Fall eine signifikante Diskrepanz aufweisen, wird jedoch in der Regel für die Analyse des durchschnittlichen Falles ein großes O verwendet. Benutze großes Theta für den schlimmsten Fall. –

+0

Das ist überraschend, ich hätte erwartet, dass der schlimmste Fall der typischere Einsatz wäre, obwohl (besonders beim Hashing) der schlimmste Fall häufig auftreten würde, wäre wahrscheinlich eine Motivation, nach einem besseren Algorithmus zu suchen. Ich kann sicherlich sehen, wo der allgemeine/durchschnittliche Fall jedoch nützlich wäre. Im Fall von Hashing würde ich O (1) die meiste Zeit erwarten. – ThatBlairGuy

7

Im Allgemeinen ist es O (1).

+0

Auch unter Berücksichtigung der bekannten schlechten Performance der eingebauter 'GetHashCode'? Ich würde nicht davon abhängen, dass es O (1) ist ... –

+2

@Stephen: Wovon sprichst du? Auch wenn "GetHashCode" eine Stunde braucht, um zurückzukehren, ist es immer noch O (1) - die Leistung von "GetHashCode" skaliert nicht mit der Größe des Satzes. – SLaks

+0

@SLaks, ich würde vermuten, Stephen bezog sich auf die schlechte Eignung der Standardimplementierung für Hashing. Siehe http://stackoverflow.com/questions/720177/default-implementation-for-object-gethashcode/720196#720196 –

5

Nein, Big O definiert nicht "Worst Case", es definiert ein Limit. Hash-basierte Suchvorgänge (mit guten Hashing-Algorithmen, die eine effiziente Werteverteilung und eine geringe Kollisionsrate bieten) werden mit steigender Anzahl von Elementen zu einem konstanten Wert fortlaufend (sie erreichen niemals oder diesen konstanten Wert, aber das ist der Punkt, der eine Grenze darstellt)).

2

Ich glaube, es bedeutet O (1) im Durchschnitt.

0

Mein Verständnis von Big Oh ist, dass der "schlimmste Fall" im Allgemeinen in Bezug auf die Anzahl der beteiligten Elemente ist. Wenn also eine Funktion O (n) mit 10 Elementen, aber O (n Quadrat) mit 100 oder mehr ausführen würde (nicht sicher, ob ein solcher Algorithmus tatsächlich existiert), dann wird der Algorithmus als O (n-Quadrat) betrachtet.

0

O (1) bedeutet nicht unbedingt "schlimmster Fall". Bei Hashes sagt man normalerweise, dass die "erwartete" Nachschlagezeit O (1) ist, da die Wahrscheinlichkeit von Hash-Kollisionen gering ist.

+0

Das hat mich überrascht - die Phrasierung an den verschiedenen Stellen, an denen ich Referenzen fand, sagte nicht "erwartet" oder "typisch". Sie sagten "ist", was immer bedeutet. – ThatBlairGuy

6

Für eine ordnungsgemäß implementierte Hash-Tabelle haben Lookups amortized konstante Zeit Komplexität.

In der Praxis kann ein einzelnes Nachschlagen bei Kollisionen O (n) sein, wie Sie sagen. Wenn Sie jedoch eine große Anzahl von Suchvorgängen durchführen, ist die durchschnittliche Zeitkomplexität pro Operation konstant.

Zitiert wikipedia:

Amortisierte Analyse unterscheidet sich von der durchschnittlichen Fall Leistung in diese Wahrscheinlichkeit nicht beteiligt ist; Die amortisierte Analyse garantiert die Zeit pro Vorgang gegenüber der Worst-Case-Leistung.

Die Methode erfordert Kenntnisse, welche Reihe von Operationen möglich sind. Dies ist am häufigsten bei Datenstrukturen der Fall, bei denen der Zustand zwischen den Operationen bestehen bleibt. Der Grundgedanke ist, dass eine Worst-Case-Operation den Zustand so verändern kann, dass der Worst-Case lange nicht mehr auftreten kann und die Kosten "amortisieren".

+1

+1, schließlich der alles wichtige Begriff "amortisiert". –

+0

In der Tat, amortisierte Komplexität muss in einer guten Beschreibung der Hash-Tabelle Komplexität erwähnt werden. Beachten Sie jedoch, dass die amortisierte O (1) -Komplexität eine Annahme voraussetzt, dass die Schlüssel ausreichend zufällig verteilt sind. Wenn ein Angreifer die Schlüssel auswählt, die dem Hash hinzugefügt werden, kann er jedes Mal eine Kollision erzwingen. Dies könnte durch Verwendung eines kryptografischen Hashes vermieden werden, aber diese sind sehr teuer, so dass Sie konstante Zeit mit einer unerschwinglich großen Konstante erhalten. Eine andere Möglichkeit besteht darin, einen Zufalls-Seed in den Hash einzufügen (Perl hat dies irgendwann getan). – Gilles

1

Nein, Big-O-Notation ist nicht unbedingt auf den Worst-Case beschränkt. Normalerweise wird Big-O für den besten Fall, den Durchschnitts- und den schlimmsten Fall veröffentlicht. Es ist nur so, dass die meisten Menschen sich auf den schlimmsten Fall konzentrieren. Außer im Falle einer Hashtabelle passiert der schlimmste Fall selten, so dass die Verwendung des Durchschnittsfalls sinnvoller ist.

Ja, eine gute Hash-Funktion reduziert die Wahrscheinlichkeit einer Kollision. Eine ungültige Hash-Funktion kann den Clustering-Effekt verursachen (bei dem unterschiedliche Werte auf denselben Wert oder denselben Wert synchronisiert werden). Es ist leicht zu demonstrieren, dass HashSet tatsächlich O (n) werden kann, indem die GetHashCode Funktion in einer solchen Weise implementiert wird, dass sie die ganze Zeit den gleichen Wert zurückgibt.

In einem nutshull, ja HashSet und Dictionary kann beschrieben werden als mit O (1) Laufzeit Komplexität, weil der Schwerpunkt auf dem durchschnittlichen Fall-Szenario ist.

Big-O kann übrigens auch zur Analyse der amortisierten Komplexität verwendet werden. Amortisierte Komplexität ist, wie sich eine Folge separater (und manchmal sogar unterschiedlicher) Operationen verhält, wenn sie zusammen gruppiert werden, als wären sie eine große Operation. Zum Beispiel wird gesagt, dass ein gespreizter Baum eine amortisierte O (log (n)) Such-, Einfügungs- und Löschungskomplexität hat, obwohl der schlechteste Fall für jedes O (n) und der beste Fall O (1) ist.

0

Hash-Tabellen haben nicht nur eine durchschnittliche Fall-Performance O (1), aber wenn die Hash-Funktion zufällig ist, für einen bestimmten Prozentsatz P < 100%, die Leistung, die P% der Zeit von einem richtig erhalten werden kann entworfene Hash-Geschichte ist O (1). Obwohl die extremen parasitären Fälle mit steigendem N immer stärker werden, wird dies durch die Tatsache ausgeglichen, dass selbst mäßig parasitäre Fälle immer weniger wahrscheinlich werden.

Verwandte Themen