Ich möchte zufällig ein Element aus einem Array auswählen, aber jedes Element hat eine bekannte Auswahlwahrscheinlichkeit.
Alle Chancen zusammen (innerhalb des Arrays) summieren sich zu 1.
Welchen Algorithmus würden Sie als den schnellsten und am besten geeigneten für große Berechnungen vorschlagen?
Beispiel:
id => chance
array[
0 => 0.8
1 => 0.2
]
Für diesen Pseudocode sollte der betreffende Algorithmus bei mehreren Aufrufen statistisch vier Elemente auf ID 0
für ein Element auf ID zurückgeben 1
.
log2(500) = 9
Schritte pro Suche dauern .lower_bound()
in C ++ oderbisect_left()
in Python .Der Algorithmus ist einfach
quelle
Ich habe festgestellt, dass dieser Artikel am nützlichsten ist, um dieses Problem vollständig zu verstehen. Diese Stackoverflow-Frage ist möglicherweise auch das, wonach Sie suchen.
Ich glaube, die optimale Lösung ist die Verwendung der Alias-Methode (Wikipedia) . Es erfordert O (n) Zeit zum Initialisieren, O (1) Zeit zum Treffen einer Auswahl und O (n) Speicher.
Hier ist der Algorithmus zum Generieren des Ergebnisses des Würfelns eines gewichteten n- seitigen Chips (von hier aus ist es trivial, ein Element aus einem Array mit einer Länge von n auszuwählen ), wie aus diesem Artikel entnommen . Der Autor geht davon aus, dass Sie Funktionen zum Werfen eines fairen Würfels (
floor(random() * n)
) und zum Werfen einer voreingenommenen Münze (random() < p
) haben.quelle
Ein weiteres Ruby-Beispiel:
def weighted_rand(weights = {}) raise 'Probabilities must sum up to 1' unless weights.values.inject(&:+) == 1.0 raise 'Probabilities must not be negative' unless weights.values.all? { |p| p >= 0 } # Do more sanity checks depending on the amount of trust in the software component using this method # E.g. don't allow duplicates, don't allow non-numeric values, etc. # Ignore elements with probability 0 weights = weights.reject { |k, v| v == 0.0 } # e.g. => {"a"=>0.4, "b"=>0.4, "c"=>0.2} # Accumulate probabilities and map them to a value u = 0.0 ranges = weights.map { |v, p| [u += p, v] } # e.g. => [[0.4, "a"], [0.8, "b"], [1.0, "c"]] # Generate a (pseudo-)random floating point number between 0.0(included) and 1.0(excluded) u = rand # e.g. => 0.4651073966724186 # Find the first value that has an accumulated probability greater than the random number u ranges.find { |p, v| p > u }.last # e.g. => "b" end
Wie benutzt man:
weights = {'a' => 0.4, 'b' => 0.4, 'c' => 0.2, 'd' => 0.0} weighted_rand weights
Was ungefähr zu erwarten ist:
sample = 1000.times.map{ weighted_rand weights } sample.count('a') # 396 sample.count('b') # 406 sample.count('c') # 198 sample.count('d') # 0
quelle
weighted_rand
Methode aktualisiert , um das von Ihnen beschriebene Problem zu beheben.Ein Beispiel in Rubin
quelle
Dies kann in O (1) erwarteter Zeit pro Probe wie folgt erfolgen.
Berechnen Sie die CDF F (i) für jedes Element i als die Summe der Wahrscheinlichkeiten kleiner oder gleich i.
Definieren Sie den Bereich r (i) eines Elements i als Intervall [F (i - 1), F (i)].
Erstellen Sie für jedes Intervall [(i - 1) / n, i / n] einen Bucket, der aus der Liste der Elemente besteht, deren Bereich das Intervall überlappt. Dies dauert insgesamt O (n) Zeit für das gesamte Array, solange Sie einigermaßen vorsichtig sind.
Wenn Sie das Array zufällig auswählen, berechnen Sie einfach, in welchem Bucket sich die Zufallszahl befindet, und vergleichen es mit jedem Element der Liste, bis Sie das Intervall finden, in dem es enthalten ist.
Die Kosten einer Stichprobe betragen O (die erwartete Länge einer zufällig ausgewählten Liste) <= 2.
quelle
Dies ist ein PHP-Code, den ich in der Produktion verwendet habe:
quelle
Rubinlösung mit dem Pickup Gem :
Beispiel:
gab Ausgabe:
quelle
Wenn das Array klein ist, würde ich dem Array eine Länge von in diesem Fall fünf geben und die entsprechenden Werte zuweisen:
quelle
Der Trick könnte darin bestehen, ein Hilfsarray mit Elementwiederholungen abzutasten, die die Wahrscheinlichkeit widerspiegeln
In Anbetracht der mit ihrer Wahrscheinlichkeit verbundenen Elemente als Prozentsatz:
Wenn Sie so allgemein wie möglich sein möchten, müssen Sie den Multiplikator basierend auf der maximalen Anzahl von Bruchziffern berechnen und anstelle von 100 verwenden:
quelle
"Glücksrad" O (n), nur für kleine Arrays verwenden:
quelle
Ich würde mir vorstellen, dass Zahlen größer oder gleich 0,8, aber kleiner als 1,0 das dritte Element auswählen.
Mit anderen Worten:
x ist eine Zufallszahl zwischen 0 und 1
wenn 0,0> = x <0,2: Punkt 1
wenn 0,2> = x <0,8: Punkt 2
wenn 0,8> = x <1,0: Punkt 3
quelle
Ich werde die Antwort auf https://stackoverflow.com/users/626341/masciugo verbessern .
Grundsätzlich erstellen Sie ein großes Array, bei dem die Häufigkeit, mit der ein Element angezeigt wird, proportional zum Gewicht ist.
Es hat einige Nachteile.
Um dem entgegenzuwirken, tun Sie Folgendes.
Erstellen Sie ein solches Array, fügen Sie jedoch nur zufällig ein Element ein. Die Wahrscheinlichkeit, dass ein Element eingefügt wird, ist proportional zum Gewicht.
Wählen Sie dann ein zufälliges Element aus dem Üblichen aus.
Wenn es also 3 Elemente mit unterschiedlichem Gewicht gibt, wählen Sie einfach ein Element aus einem Array von 1-3 Elementen aus.
Probleme können auftreten, wenn das konstruierte Element leer ist. Das heißt, es kommt einfach vor, dass keine Elemente im Array angezeigt werden, weil ihre Würfel unterschiedlich würfeln.
In diesem Fall schlage ich vor, dass die Wahrscheinlichkeit, mit der ein Element eingefügt wird, p (eingefügt) = wi / wmax ist.
Auf diese Weise wird ein Element eingefügt, nämlich das mit der höchsten Wahrscheinlichkeit. Die anderen Elemente werden durch die relative Wahrscheinlichkeit eingefügt.
Angenommen, wir haben 2 Objekte.
Element 1 wird in 0,20% der Fälle angezeigt. Element 2 zeigt 0,40% der Zeit und hat die höchste Wahrscheinlichkeit.
Im Bereich wird Element 2 ständig angezeigt. Element 1 wird die halbe Zeit angezeigt.
Element 2 wird also zweimal so oft wie Element 1 genannt. Der Allgemeinheit halber werden alle anderen Elemente proportional zu ihrem Gewicht genannt. Auch die Summe aller ihrer Wahrscheinlichkeiten ist 1, da das Array immer mindestens 1 Element hat.
quelle