Das sollte bedeuten, dass der Container tatsächlich Kollisionen auflöst. Diese Seite sagt mir jedoch nicht, wie es das macht. Ich kenne einige Methoden, um Kollisionen zu lösen, wie z. B. die Verwendung von verknüpften Listen und/oder das Sondieren. Was ich wissen möchte, ist, wie die c++ STL unordered_map es auflöst.
Es ist implementierungsabhängig. Eine Sprachreferenz sagt Ihnen nicht, wie es gemacht wird, weil es im Standard nicht spezifiziert ist, wie es gemacht werden sollte.
– Benjamin Lindley
3. Februar 14 um 2:04 Uhr
libstdc++ verwendet lineare Verkettung, aber andere Implementierungen der STL können andere Techniken verwenden
– Brian Bi
3. Februar 14 um 2:09 Uhr
Technisch gesehen verwenden wir die STL überhaupt nicht. Wir verwenden die C++-Standardbibliothek, die eine Schnittstellenspezifikation ist. Und ja, es hat mehrere Implementierungen.
– Benjamin Lindley
3. Februar 14 um 2:18 Uhr
Nein, die STL war vor Jahrzehnten eine Bibliothek, in der Ideen gestohlen und in die C++-Standardbibliothek integriert wurden. Jeder Compiler von C++ muss die Standardbibliothek auf beliebige Weise implementieren. Die Leute neigen dazu, die von der STL inspirierten Teile der Standardbibliothek als STL zu bezeichnen, wenn auch falsch.
– Yakk – Adam Nevraumont
3. Februar 14 um 3:45 Uhr
“Keine zwei Elemente im Container können gleichwertige Schlüssel haben” ist eine Bedingung für Pred Parameter von unordered_map. Der Begriff der Kollisionen gilt für Hash Parameter. Zwei Schlüssel sind möglicherweise nicht äquivalent, können aber dennoch auf denselben Wert gehasht werden – die eigentliche Definition einer Hash-Kollision.
– Igor Tandetnik
3. Februar 14 um 4:02 Uhr
Jerry Sarg
Der Standard definiert etwas mehr darüber, als die meisten Menschen zu erkennen scheinen.
Insbesondere fordert der Standard (§23.2.5/9):
Die Elemente eines ungeordneten assoziativen Containers sind in Buckets organisiert. Schlüssel mit demselben Hashcode erscheinen im selben Bucket.
Die Schnittstelle umfasst a bucket_count das läuft in konstanter Zeit. (Tabelle 103). Dazu gehört auch ein bucket_size das muss zeitlich linear zur Größe des Eimers laufen.
Das beschreibt im Grunde eine Implementierung, die Kollisionsverkettung verwendet. Wenn Sie Kollisionsverkettung verwenden, ist das Erfüllen aller Anforderungen irgendwo zwischen einfach und trivial. bucket_count() ist die Anzahl der Elemente in Ihrem Array. bucket_size() ist die Anzahl der Elemente in der Kollisionskette. Sie in konstanter bzw. linearer Zeit zu erhalten, ist einfach und unkompliziert.
Wenn Sie dagegen so etwas wie lineares Sondieren oder doppeltes Hashing verwenden, werden diese Anforderungen so gut wie unmöglich zu erfüllen. Insbesondere müssen alle Elemente, die auf einen bestimmten Wert gehasht wurden, im selben Bucket landen, und Sie müssen in der Lage sein, diese Buckets in konstanter Zeit zu zählen.
Wenn Sie jedoch so etwas wie lineares Sondieren oder doppeltes Hashing verwenden, bedeutet das Finden aller Elemente, die auf denselben Wert gehasht wurden, dass Sie den Wert hashen und dann durch die “Kette” nicht leerer Elemente in Ihrer Tabelle gehen müssen, um herauszufinden, wie viele von denen auf den gleichen Wert gehasht. Das ist jedoch nicht linear in Bezug auf die Anzahl der Elemente, die auf denselben Wert gehasht wurden – es ist linear in Bezug auf die Anzahl der Elemente, die auf denselben Wert gehasht wurden oder ein kollidierender Wert.
Mit genügend zusätzlicher Arbeit und einer ziemlichen Menge an dehnender Bedeutung einiger der Anforderungen fast bis zum Zerreißen, ist es möglicherweise kaum möglich, eine Hash-Tabelle mit etwas anderem als Kollisionsverkettung zu erstellen und dennoch die Anforderungen zumindest irgendwie zu erfüllen. -aber ich bin mir nicht sicher, ob es möglich ist, und es würde sicherlich eine Menge zusätzlicher Arbeit erfordern.
Zusammenfassung: alle praktischen Umsetzungen von std::unordered_set (oder unordered_map) verwenden zweifellos Kollisionsverkettung. Während es vielleicht (gerade noch) möglich ist, die Anforderungen mit linearem Sondieren oder doppeltem Hashing zu erfüllen, scheint eine solche Implementierung viel zu verlieren und im Gegenzug fast nichts zu gewinnen.
„Die Auflage ist noch zu erfüllen [using linear probing or double hashing]” / “Dann gehst du durch die “Kette” der belegten Gegenstände, um einen Platz zu finden, an dem du diesen Gegenstand einfügen kannst.” ist nicht kompatibel mit der Anforderung “Schlüssel mit demselben Hash-Code erscheinen im selben Eimer.” Natürlich , es ist fraglich, ob die Kollisionsverkettung sowieso etwas “in denselben Eimer” bringt, aber Sie sind es deutlich Vorschlag, Schlüssel mit demselben Hash in verschiedene Buckets zu legen. 23.2.5/9 scheint eine unglückliche Einschränkung zu sein … Sondieren ist für mehr als “niedrigen Lastfaktor” nützlich – viele Vorteile ergeben sich aus der Vermeidung von Haufen ….
– Toni Delroy
3. Februar 14 um 4:46 Uhr
@ TonyD: nicht wirklich – in diesem Fall ist ein “Eimer” keine physische Sache mehr, sondern einfach alle Slots, die auf denselben Wert gehasht wurden. Sie müssten genug Intelligenz in local_iterator einbauen, um kollidierende Elemente zu überspringen, aber es scheint mir immer noch durchaus möglich zu sein.
– Jerry Sarg
3. Februar 14 um 4:55 Uhr
Wenn es so flexibel ist, „tauchen Schlüssel mit demselben Hash-Code im selben Bucket auf“. hat überhaupt keine Bedeutung. Stimmen Sie zu, nicht zuzustimmen usw. Prost.
– Toni Delroy
3. Februar 14 um 5:21 Uhr
@TonyD: Nicht wirklich. Ein Bucket ist die Menge von Elementen mit Schlüsseln, die zu einem bestimmten Wert gehasht werden und die mit einem local_iterator iteriert werden können. Angesichts der Tatsache, dass sie es ausdrücklich vermeiden, eine Implementierung anzugeben, ist dies wirklich der Fall sollte nicht viel über die Umsetzung aus.
– Jerry Sarg
3. Februar 14 um 5:24 Uhr
Nun, eine einvernehmliche verbindliche Definition von “Bucket” von zB Dijkstra zu finden, ist bei Google nicht einfach, also überlegen wir uns einfach, ob Ihre postulierte Implementierung andere Anforderungen erfüllen kann. Nach Ihrer Begründung für die Notwendigkeit count, b.find(k) kann nicht Worst-Case sein O(b.size()) wo b ist ein “logischer” Bucket size(). Weiter, b.begin(n), b.end(n) et al. müssen eine konstante Komplexität aufweisen und müssten daher neben Ihren gespeichert werden count am ursprünglich gehashten Bucket.
– Toni Delroy
3. Februar 14 um 6:30 Uhr
Ich habe diese Antwort gefunden, als ich suchte, wie ich erkennen kann, wann meine Typen kollidieren, also werde ich dies posten, falls dies die Absicht der Frage ist.:
Ich glaube, es gibt ein Missverständnis über “Eindeutige Schlüssel. Keine zwei Elemente im Container können gleichwertige Schlüssel haben.”
schau dir den Code unten an
//pseudocode
std::unordered_map<int, char> hashmap;
hashmap[5] = 'a';
hashmap[5] = 'b'; //replace 'a' with 'b', there is no collision being handled.
Ich denke, die Antwort von Jerry bezieht sich auf das interne System, das es verwendet, um Schlüssel auf geeignete Array-Indizes zu verkleinern.
Wenn Sie möchten, dass Kollisionen für Ihre Typen (mit Buckets) behandelt werden, müssen Sie std::unordered_multimap und müssen iterieren
Hoffentlich kann dieser Code ohne den Kontext gelesen werden, mit dem ich ihn generiert habe. Es prüft im Grunde, ob ein Element im Bucket, das dem Hash zugeordnet ist, das Element ist, nach dem ich suche.
//sp is std::shared_ptr
//memo is std::unordered_multimap< int, sp<AStarNode> >
//there's probably multiple issues with this code in terms of good design (like using int keys rather than unsigned)
bool AStar_Incremental::hasNodeBeenVisited(sp<AStarNode> node)
{
using UMIter = std::unordered_multimap<int, sp<AStarNode> >::iterator;
bool bAlreadyVisited = false;
//get all values for key in O(1*)
int hash = WorldGrid::hashGrid(node->location);
std::pair<UMIter, UMIter> start_end = memo.equal_range(hash); //bucket range
UMIter start = start_end.first;
UMIter end = start_end.second;
//hopefully this is implemented to be O(m) where m is the bucket size.
for(UMIter bucketIter = start; bucketIter != end; ++bucketIter)
{
sp<AStarNode> previousNode = bucketIter->second;
sf::Vector2i& previousVisit = previousNode->location;
if (previousVisit == node->location)
{
bAlreadyVisited = true;
break;
}
}
return bAlreadyVisited;
}
.
7589600cookie-checkWie löst C++ STL unordered_map Kollisionen?yes
Es ist implementierungsabhängig. Eine Sprachreferenz sagt Ihnen nicht, wie es gemacht wird, weil es im Standard nicht spezifiziert ist, wie es gemacht werden sollte.
– Benjamin Lindley
3. Februar 14 um 2:04 Uhr
libstdc++ verwendet lineare Verkettung, aber andere Implementierungen der STL können andere Techniken verwenden
– Brian Bi
3. Februar 14 um 2:09 Uhr
Technisch gesehen verwenden wir die STL überhaupt nicht. Wir verwenden die C++-Standardbibliothek, die eine Schnittstellenspezifikation ist. Und ja, es hat mehrere Implementierungen.
– Benjamin Lindley
3. Februar 14 um 2:18 Uhr
Nein, die STL war vor Jahrzehnten eine Bibliothek, in der Ideen gestohlen und in die C++-Standardbibliothek integriert wurden. Jeder Compiler von C++ muss die Standardbibliothek auf beliebige Weise implementieren. Die Leute neigen dazu, die von der STL inspirierten Teile der Standardbibliothek als STL zu bezeichnen, wenn auch falsch.
– Yakk – Adam Nevraumont
3. Februar 14 um 3:45 Uhr
“Keine zwei Elemente im Container können gleichwertige Schlüssel haben” ist eine Bedingung für
Pred
Parameter vonunordered_map
. Der Begriff der Kollisionen gilt fürHash
Parameter. Zwei Schlüssel sind möglicherweise nicht äquivalent, können aber dennoch auf denselben Wert gehasht werden – die eigentliche Definition einer Hash-Kollision.– Igor Tandetnik
3. Februar 14 um 4:02 Uhr