Wie wähle ich ein zufälliges Element aus einer Menge aus? Ich bin besonders daran interessiert, ein zufälliges Element aus einem HashSet oder einem LinkedHashSet in Java auszuwählen. Lösungen für andere Sprachen sind ebenfalls willkommen.
179
Antworten:
quelle
Ein etwas verwandtes Wussten Sie schon:
Es gibt nützliche Methoden
java.util.Collections
zum Mischen ganzer Sammlungen:Collections.shuffle(List<?>)
undCollections.shuffle(List<?> list, Random rnd)
.quelle
List
Schnittstelle erweitern, nicht für dieSet
vom OP diskutierte Schnittstelle.Schnelle Lösung für Java mit einem
ArrayList
und einemHashMap
: [Element -> Index].Motivation: Ich brauchte eine Reihe von Elementen mit
RandomAccess
Eigenschaften, insbesondere um ein zufälliges Element aus der Gruppe auszuwählen (siehepollRandom
Methode). Die zufällige Navigation in einem Binärbaum ist nicht genau: Bäume sind nicht perfekt ausbalanciert, was nicht zu einer gleichmäßigen Verteilung führen würde.quelle
Concurrent
sind wirklich sicher, diejenigen, die damit umwickeltCollections.synchronized()
sind, sind halb sicher. Auch das OP hat nichts über Parallelität gesagt, daher ist dies eine gültige und gute Antwort.dta
(dies kann über Guaven erreicht werdenIterators.unmodifiableIterator
). Andernfalls werden die Standardimplementierungen von z. B. removeAll und RetainAll in AbstractSet und seinen Eltern, die mit diesem Iterator arbeiten, Ihre Probleme lösenRandomSet
!Dies ist schneller als die for-each-Schleife in der akzeptierten Antwort:
Das for-each-Konstrukt ruft
Iterator.hasNext()
jede Schleife auf, aber seitdemindex < set.size()
diese Überprüfung unnötig ist. Ich sah eine Geschwindigkeitssteigerung von 10-20%, aber YMMV. (Außerdem wird dies kompiliert, ohne dass eine zusätzliche return-Anweisung hinzugefügt werden muss.)Beachten Sie, dass dieser Code (und die meisten anderen Antworten) auf jede Sammlung angewendet werden kann, nicht nur auf Set. In generischer Methodenform:
quelle
Wenn Sie dies in Java tun möchten, sollten Sie in Betracht ziehen, die Elemente in eine Sammlung mit wahlfreiem Zugriff (z. B. eine ArrayList) zu kopieren. Denn der Zugriff auf das ausgewählte Element ist teuer (O (n) anstelle von O (1)), es sei denn, Ihr Satz ist klein. [ed: Listenkopie ist auch O (n)]
Alternativ können Sie nach einer anderen Set-Implementierung suchen, die Ihren Anforderungen besser entspricht. Das ListOrderedSet aus Commons Collections sieht vielversprechend aus.
quelle
In Java 8:
quelle
In Java:
quelle
quelle
Dies ist identisch mit der akzeptierten Antwort (Khoth), jedoch werden die unnötigen
size
undi
Variablen entfernt.Obwohl die beiden oben genannten Variablen wegfallen, bleibt die obige Lösung immer noch zufällig, da wir uns auf zufällig (beginnend mit einem zufällig ausgewählten Index) verlassen, um sich
0
über jede Iteration hinweg zu dekrementieren .quelle
if (--random < 0) {
, worandom
erreicht-1
.Clojure-Lösung:
quelle
nth
Element ebenfalls durchlaufen müssen , um das Element zu erhaltenseq
.Perl 5
Hier ist eine Möglichkeit, dies zu tun.
quelle
C ++. Dies sollte relativ schnell gehen, da es nicht erforderlich ist, den gesamten Satz zu durchlaufen oder zu sortieren. Dies sollte bei den meisten modernen Compilern sofort funktionieren , vorausgesetzt, sie unterstützen tr1 . Wenn nicht, müssen Sie möglicherweise Boost verwenden.
Die Boost-Dokumente sind hier hilfreich, um dies zu erklären, auch wenn Sie Boost nicht verwenden.
Der Trick besteht darin, die Tatsache zu nutzen, dass die Daten in Buckets unterteilt wurden, und schnell einen zufällig ausgewählten Bucket zu identifizieren (mit der entsprechenden Wahrscheinlichkeit).
quelle
Die obige Lösung bezieht sich auf die Latenz, garantiert jedoch nicht die gleiche Wahrscheinlichkeit, dass jeder Index ausgewählt wird.
Wenn dies berücksichtigt werden muss, versuchen Sie es mit einer Probenahme im Reservoir. http://en.wikipedia.org/wiki/Reservoir_sampling .
Collections.shuffle () (wie von wenigen vorgeschlagen) verwendet einen solchen Algorithmus.
quelle
Da Sie sagten "Lösungen für andere Sprachen sind ebenfalls willkommen", ist hier die Version für Python:
quelle
Können Sie nicht einfach die Größe / Länge der Menge / des Arrays ermitteln, eine Zufallszahl zwischen 0 und der Größe / Länge generieren und dann das Element aufrufen, dessen Index mit dieser Zahl übereinstimmt? HashSet hat eine .size () -Methode, da bin ich mir ziemlich sicher.
Im Pseudocode -
quelle
PHP unter der Annahme, dass "set" ein Array ist:
Die Mersenne Twister-Funktionen sind besser, aber es gibt kein MT-Äquivalent zu array_rand in PHP.
quelle
Icon hat einen Set-Typ und einen Zufallselement-Operator, unäres "?", Also den Ausdruck
erzeugt eine Zufallszahl zwischen 1 und 5.
Der zufällige Startwert wird beim Ausführen eines Programms auf 0 initialisiert, um bei jeder Ausführung unterschiedliche Ergebnisse zu erzielen
randomize()
quelle
In C #
quelle
Javascript-Lösung;)
Oder alternativ:
quelle
In lisp
quelle
ELT
könnte für jede Sequenz gearbeitet werden.In Mathematica:
Oder in neueren Versionen einfach:
Dies wurde abgelehnt, vielleicht weil es keine Erklärung gibt. Hier ist eine:
Random[]
erzeugt einen Pseudozufalls-Float zwischen 0 und 1. Dieser wird mit der Länge der Liste multipliziert und dann wird die Deckenfunktion verwendet, um auf die nächste Ganzzahl aufzurunden. Dieser Index wird dann extrahierta
.Da die Funktionalität von Hash-Tabellen in Mathematica häufig mit Regeln ausgeführt wird und Regeln in Listen gespeichert werden, kann Folgendes verwendet werden:
quelle
Wie wäre es einfach
quelle
Zum Spaß habe ich ein RandomHashSet geschrieben, das auf Ablehnungsstichproben basiert. Es ist ein bisschen hackig, da wir mit HashMap nicht direkt auf die Tabelle zugreifen können, aber es sollte gut funktionieren.
Es wird kein zusätzlicher Speicher verwendet und die Suchzeit wird mit O (1) amortisiert. (Weil Java HashTable dicht ist).
quelle
Am einfachsten mit Java 8 ist:
wo
n
ist eine zufällige ganze Zahl. Natürlich ist es von geringerer Leistung als das mit demfor(elem: Col)
quelle
Mit Guave können wir etwas besser als Khoths Antwort:
quelle
PHP mit MT:
quelle
Sie können das Set auch auf ein Array übertragen. Verwenden Sie das Array. Es wird wahrscheinlich im kleinen Maßstab funktionieren. Ich sehe, dass die for-Schleife in der am häufigsten gewählten Antwort ohnehin O (n) ist
quelle
Wenn Sie wirklich nur "irgendein" Objekt aus dem auswählen möchten
Set
, ohne die Zufälligkeit zu garantieren, ist es am einfachsten, das erste vom Iterator zurückgegebene zu nehmen.quelle
Eine generische Lösung, die Khoths Antwort als Ausgangspunkt verwendet.
quelle
Leider kann dies in keinem der Set-Container der Standardbibliothek effizient (besser als O (n)) durchgeführt werden.
Dies ist seltsam, da es sehr einfach ist, Hash-Sets und Binär-Sets eine zufällige Auswahlfunktion hinzuzufügen. In einem nicht zu spärlichen Hash-Set können Sie zufällige Einträge versuchen, bis Sie einen Treffer erhalten. Für einen Binärbaum können Sie zufällig zwischen dem linken oder rechten Teilbaum mit maximal O (log2) Schritten wählen. Ich habe eine Demo der folgenden implementiert:
Ich habe [995, 975, 971, 995, 1057, 1004, 966, 1052, 984, 1001] als Ausgabe erhalten, sodass die Verteilungsnähte gut sind.
Ich habe mit dem gleichen Problem für mich selbst zu kämpfen, und ich habe noch nicht entschieden, ob der Leistungsgewinn dieser effizienteren Auswahl den Aufwand für die Verwendung einer Python-basierten Sammlung wert ist. Ich könnte es natürlich verfeinern und in C übersetzen, aber das ist mir heute zu viel Arbeit :)
quelle