Ich musste eine gewichtete Version von random.choice schreiben (jedes Element in der Liste hat eine andere Wahrscheinlichkeit, ausgewählt zu werden). Folgendes habe ich mir ausgedacht:
def weightedChoice(choices):
"""Like random.choice, but each element can have a different chance of
being selected.
choices can be any iterable containing iterables with two items each.
Technically, they can have more than two items, the rest will just be
ignored. The first item is the thing being chosen, the second item is
its weight. The weights can be any numeric values, what matters is the
relative differences between them.
"""
space = {}
current = 0
for choice, weight in choices:
if weight > 0:
space[current] = choice
current += weight
rand = random.uniform(0, current)
for key in sorted(space.keys() + [current]):
if rand < key:
return choice
choice = space[key]
return None
Diese Funktion erscheint mir zu komplex und hässlich. Ich hoffe, dass jeder hier einige Vorschläge zur Verbesserung oder alternative Möglichkeiten dazu machen kann. Effizienz ist für mich nicht so wichtig wie Code-Sauberkeit und Lesbarkeit.
quelle
random.choices
bei einzelnen Anrufen. Wenn Sie viele zufällige Ergebnisse benötigen, ist es wirklich wichtig, alle auf einmal durch Anpassen auszuwählennumber_of_items_to_pick
. Wenn Sie dies tun, ist es eine Größenordnung schneller.len(list_of_candidates)
und dannlist_of_candidates[draw]
Seit Python 3.6 gibt es eine Methode
choices
aus demrandom
Modul.Beachten Sie, dass
random.choices
ein Beispiel mit Ersatz gemäß den folgenden Dokumenten erstellt wird :Wenn Sie ersatzlos probieren müssen, können Sie, wie in der brillanten Antwort von @ ronan-paixão angegeben , verwenden
numpy.choice
, dessenreplace
Argument ein solches Verhalten steuert.quelle
random.choices
dies nicht tut. Bei einer winzigen Liste mit 8 Elementen ist dies natürlich langsamer. Wenn Sie aus einer solchen Liste 10.000 Mal auswählen, haben Sie Recht. Aber in Fällen, in denen die Liste größer ist (je nachdem, wie Sie testen, sehe ich Unterbrechungspunkte zwischen 100 und 300 Elementen),np.random.choice
beginnt die Outperformancerandom.choices
durch eine ziemlich große Lücke. Zum Beispiel, einschließlich des Normalisierungsschritts zusammen mit dem Numpy-Aufruf, erhalte ich eine fast 4-fache Beschleunigungrandom.choices
für eine Liste von 10.000 Elementen.quelle
upto +=w; if upto > r
if r < 0
r <= 0
. Betrachten Sie einen Eingabesatz von 1 Elementen und einen Wurf von 1,0. Die Behauptung wird dann fehlschlagen. Ich habe diesen Fehler in der Antwort korrigiert.# pragma: no branch
0.0 <= x < total
.Wenn Sie mehr als eine Auswahl treffen müssen, teilen Sie diese in zwei Funktionen auf, eine zum Erstellen der kumulativen Gewichte und eine zum Halbieren auf einen zufälligen Punkt.
quelle
O(n)
aufgrund der kumulativen Verteilungsberechnung immer noch ein.random()
kann 1.0 nicht zurückgeben. Gemäß den Dokumenten wird ein Ergebnis im halboffenen Intervall zurückgegeben[0.0, 1.0)
, dh, es kann genau 0,0, aber nicht genau 1,0 zurückgeben. Der größte Wert, den es zurückgeben kann, ist 0,99999999999999988897769753748434595763683319091796875 (Python druckt als 0,999999999999999999 und ist der größte 64-Bit-Float kleiner als 1).Wenn es Ihnen nichts ausmacht, numpy zu verwenden, können Sie numpy.random.choice verwenden .
Beispielsweise:
Wenn Sie wissen, wie viele Auswahlen Sie im Voraus treffen müssen, können Sie dies ohne eine Schleife wie die folgende tun:
quelle
Roh, kann aber ausreichen:
Funktioniert es?
Drucke:
Angenommen, alle Gewichte sind ganze Zahlen. Sie müssen nicht 100 addieren, ich habe das nur getan, um die Testergebnisse leichter interpretieren zu können. (Wenn Gewichte Gleitkommazahlen sind, multiplizieren Sie sie alle wiederholt mit 10, bis alle Gewichte> = 1 sind.)
quelle
[[]]*10
- alle Elemente in der äußeren Liste zeigen auf dieselbe Liste.int
immer noch viele Verweise auf dasselbe Objekt erhalten, indem Sie Folgendes tun[id(x) for x in ([99**99] * 100)]
und beobachten, dassid
bei jedem Aufruf dieselbe Speicheradresse zurückgegeben wird.Wenn Sie ein gewichtetes Wörterbuch anstelle einer Liste haben, können Sie dies schreiben
Beachten Sie, dass
[k for k in items for dummy in range(items[k])]
diese Liste erstellt wird['a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'c', 'b', 'b', 'b', 'b', 'b']
quelle
Ab Python
v3.6
kannrandom.choices
einlist
Element mit der angegebenen Größe aus der angegebenen Grundgesamtheit mit optionalen Gewichten zurückgegeben werden.Bevölkerung :
list
enthält einzigartige Beobachtungen. (Wenn leer, erhöhtIndexError
)Gewichte : Genauer gesagt sind relative Gewichte erforderlich, um eine Auswahl zu treffen.
cum_weights : kumulative Gewichte, die für die Auswahl erforderlich sind.
k : Größe (
len
) derlist
auszugebenden. (Standardlen()=1
)Einige Vorsichtsmaßnahmen:
1) Es wird eine gewichtete Stichprobe mit Ersatz verwendet, damit die gezeichneten Gegenstände später ersetzt werden. Die Werte in der Gewichtssequenz an sich spielen keine Rolle, aber ihr relatives Verhältnis spielt eine Rolle.
Anders
np.random.choice
als diejenigen, die nur Wahrscheinlichkeiten als Gewichte annehmen können und auch die Summierung einzelner Wahrscheinlichkeiten bis zu 1 Kriterium sicherstellen müssen, gibt es hier keine derartigen Regelungen. Solange sie zu numerischen Typen gehören (int/float/fraction
außerDecimal
Typ), würden diese weiterhin funktionieren.2) Wenn weder Gewichte noch cum_weights angegeben sind, wird die Auswahl mit gleicher Wahrscheinlichkeit getroffen. Wenn eine Gewichtssequenz angegeben wird, muss sie dieselbe Länge wie die Populationssequenz haben .
Wenn Sie sowohl Gewichte als auch cum_weights angeben, wird a ausgelöst
TypeError
.3) cum_weights sind normalerweise ein Ergebnis von
itertools.accumulate
Funktionen, die in solchen Situationen sehr praktisch sind.Die Lieferung
weights=[12, 12, 4]
odercum_weights=[12, 24, 28]
für unseren erfundenen Fall führt also zu demselben Ergebnis, und letzteres scheint schneller / effizienter zu sein.quelle
Hier ist die Version, die in der Standardbibliothek für Python 3.6 enthalten ist:
Quelle: https://hg.python.org/cpython/file/tip/Lib/random.py#l340
quelle
quelle
Ich bin wahrscheinlich zu spät, um etwas Nützliches beizutragen, aber hier ist ein einfacher, kurzer und sehr effizienter Ausschnitt:
Sie müssen Ihre Wahrscheinlichkeiten nicht sortieren oder einen Vektor mit Ihrer cmf erstellen und sie wird beendet, sobald sie ihre Wahl gefunden hat. Speicher: O (1), Zeit: O (N), mit durchschnittlicher Laufzeit ~ N / 2.
Wenn Sie Gewichte haben, fügen Sie einfach eine Zeile hinzu:
quelle
np.random.choice
. Interessanterweise gibt es jedoch einen Fehlermodus, bei dem eine Ausnahme ausgelöst wird. Diesprobabilities = weights / sum(weights)
garantiert nicht, dassprobabilities
sich 1 ergibt . zum Beispiel, wennweights
ist[1,1,1,1,1,1,1]
dannprobabilities
nur zu ,9999999999999998 Summe kleiner als der größtmögliche Rückgabewertrandom.random
(die ,9999999999999999 ist). Dannchoice <= cmf
ist man nie zufrieden.Wenn Ihre Liste der gewichteten Auswahlmöglichkeiten relativ statisch ist und Sie häufige Stichproben wünschen, können Sie einen O (N) -Vorverarbeitungsschritt ausführen und dann die Auswahl in O (1) mithilfe der Funktionen in dieser verwandten Antwort durchführen .
quelle
Ich habe mir den spitzen anderen Thread angesehen und mir diese Variation in meinem Codierungsstil ausgedacht. Dies gibt den Index der Wahl zum Zweck der Zählung zurück, aber es ist einfach, die Zeichenfolge zurückzugeben (kommentierte Rückgabealternative):
quelle
Dies hängt davon ab, wie oft Sie die Verteilung testen möchten.
Angenommen, Sie möchten die Verteilung K-mal abtasten. Dann ist die Zeitkomplexität, die
np.random.choice()
jedes Mal verwendet wird,O(K(n + log(n)))
wannn
die Anzahl der Elemente in der Verteilung ist.In meinem Fall musste ich dieselbe Verteilung mehrmals in der Größenordnung von 10 ^ 3 abtasten, wobei n in der Größenordnung von 10 ^ 6 liegt. Ich habe den folgenden Code verwendet, der die kumulative Verteilung vorberechnet und abtastet
O(log(n))
. Die Gesamtzeitkomplexität beträgtO(n+K*log(n))
.quelle
Eine allgemeine Lösung:
quelle
Hier ist eine andere Version von weighted_choice, die numpy verwendet. Übergeben Sie den Gewichtungsvektor und es wird ein Array von Nullen zurückgegeben, das eine 1 enthält, die angibt, welcher Behälter ausgewählt wurde. Der Code führt standardmäßig nur eine einzelne Ziehung durch. Sie können jedoch die Anzahl der auszuführenden Ziehungen übergeben, und die Anzahl pro gezogenem Behälter wird zurückgegeben.
Wenn der Gewichtungsvektor nicht 1 ergibt, wird er so normalisiert, dass dies der Fall ist.
quelle
Eine andere Möglichkeit, dies zu tun, vorausgesetzt, wir haben Gewichte am gleichen Index wie die Elemente im Elementarray.
Nehmen wir nun an, wir müssen 3 Elemente in einem Versuch ausprobieren. Sie können davon ausgehen, dass drei Kugeln R, G, B in großer Menge im Verhältnis ihrer Gewichte vorhanden sind, die durch die Gewichtsanordnung angegeben werden. Folgendes könnte möglich sein:
Sie können sich auch vorstellen, wie viele Elemente als Anzahl von Binomial- / Multinomialversuchen innerhalb eines Satzes ausgewählt werden sollen. Das obige Beispiel kann also immer noch als funktionieren
quelle
Es gibt einen Vortrag von Sebastien Thurn im kostenlosen Udacity-Kurs AI for Robotics. Grundsätzlich erstellt er mit dem Mod-Operator ein kreisförmiges Array der indizierten Gewichte
%
, setzt eine Variable Beta auf 0, wählt zufällig einen Index für Schleifen durch N, wobei N die Anzahl der Indizes ist, und erhöht in der for-Schleife zunächst Beta durch die Formel:Beta = Beta + einheitliche Stichprobe aus {0 ... 2 * Weight_max}
und dann in der for-Schleife verschachtelt, eine while-Schleife wie folgt:
Fahren Sie dann mit dem nächsten Index fort, der basierend auf den Wahrscheinlichkeiten (oder der normalisierten Wahrscheinlichkeit in dem im Kurs dargestellten Fall) erneut abgetastet werden soll.
Der Link zur Vorlesung: https://classroom.udacity.com/courses/cs373/lessons/48704330/concepts/487480820923
Ich bin mit meinem Schulkonto bei Udacity angemeldet. Wenn der Link nicht funktioniert, ist es Lektion 8, Video Nummer 21 der Künstlichen Intelligenz für Robotik, in der er Vorlesungen über Partikelfilter hält.
quelle
Wenn Sie Python 3 haben und Angst haben,
numpy
eigene Loops zu installieren oder zu schreiben, können Sie Folgendes tun:Weil Sie alles aus einer Tüte mit Sanitäradaptern bauen können ! Obwohl ... ich muss zugeben, dass Neds Antwort, obwohl sie etwas länger ist, leichter zu verstehen ist.
quelle
Eine Möglichkeit besteht darin, die Summe aller Gewichte nach dem Zufallsprinzip zu sortieren und dann die Werte als Grenzpunkte für jede Variable zu verwenden. Hier ist eine grobe Implementierung als Generator.
quelle
Mit numpy
quelle
np.random.choice
, wie in der akzeptierten Antwort erwähnt, die seit 2014 hier ist. Was bringt es , wenn Sie Ihre eigenen rollen?Ich musste so etwas wirklich schnell und einfach machen, von der Suche nach Ideen habe ich endlich diese Vorlage erstellt. Die Idee ist, die gewichteten Werte in Form eines JSON von der API zu erhalten, was hier durch das Diktat simuliert wird.
Übersetzen Sie es dann in eine Liste, in der sich jeder Wert proportional zu seinem Gewicht wiederholt, und verwenden Sie einfach random.choice, um einen Wert aus der Liste auszuwählen.
Ich habe es mit 10, 100 und 1000 Iterationen versucht. Die Verteilung scheint ziemlich solide zu sein.
quelle
Ich habe die Syntax von keinem von denen geliebt. Ich wollte wirklich nur angeben, was die Gegenstände waren und wie sie jeweils gewichtet waren. Mir ist klar, dass ich es hätte verwenden können,
random.choices
aber stattdessen habe ich schnell die folgende Klasse geschrieben.quelle
Stellen Sie random.choice () eine vorgewichtete Liste zur Verfügung:
Lösung & Test:
Ausgabe:
quelle