Wie würde ich eine Liste von N (sagen wir 100) Zufallszahlen erstellen, so dass ihre Summe 1 ist?
Ich kann eine Liste von Zufallszahlen mit erstellen
r = [ran.random() for i in range(1,100)]
Wie würde ich dies so ändern, dass die Liste 1 ergibt (dies ist für eine Wahrscheinlichkeitssimulation).
Antworten:
Die einfachste Lösung besteht in der Tat darin, N Zufallswerte zu nehmen und durch die Summe zu dividieren.
Eine allgemeinere Lösung ist die Verwendung der Dirichlet-Verteilung http://en.wikipedia.org/wiki/Dirichlet_distribution, die in numpy verfügbar ist.
Durch Ändern der Verteilungsparameter können Sie die "Zufälligkeit" einzelner Zahlen ändern
>>> import numpy as np, numpy.random >>> print np.random.dirichlet(np.ones(10),size=1) [[ 0.01779975 0.14165316 0.01029262 0.168136 0.03061161 0.09046587 0.19987289 0.13398581 0.03119906 0.17598322]] >>> print np.random.dirichlet(np.ones(10)/1000.,size=1) [[ 2.63435230e-115 4.31961290e-209 1.41369771e-212 1.42417285e-188 0.00000000e+000 5.79841280e-143 0.00000000e+000 9.85329725e-005 9.99901467e-001 8.37460207e-246]] >>> print np.random.dirichlet(np.ones(10)*1000.,size=1) [[ 0.09967689 0.10151585 0.10077575 0.09875282 0.09935606 0.10093678 0.09517132 0.09891358 0.10206595 0.10283501]]
Abhängig vom Hauptparameter gibt die Dirichlet-Verteilung entweder Vektoren an, bei denen alle Werte nahe bei 1./N liegen, wobei N die Länge des Vektors ist, oder Vektoren, bei denen die meisten Werte der Vektoren ~ 0 sind, und dort wird eine einzelne 1 sein oder etwas zwischen diesen Möglichkeiten geben.
BEARBEITEN (5 Jahre nach der ursprünglichen Antwort): Eine weitere nützliche Tatsache über die Dirichlet-Verteilung ist, dass Sie sie natürlich erhalten, wenn Sie einen Gamma-verteilten Satz von Zufallsvariablen generieren und diese dann durch ihre Summe dividieren.
quelle
[0,1/s)
). Es ist genau so einheitlich wie die nicht skalierte Verteilung, mit der Sie begonnen haben, da die Skalierung die Verteilung nicht ändert, sondern nur komprimiert. Diese Antwort gibt eine Vielzahl von Verteilungen an, von denen nur eine einheitlich ist. Wenn dies für Sie keinen Sinn ergibt, führen Sie die Beispiele aus und sehen Sie sich einige Histogramme an, um dies zu verdeutlichen. Versuchen Sie dasselbe auch mit einer Gaußschen Verteilung (np.random.normal
).Der beste Weg, dies zu tun, besteht darin, einfach eine Liste mit so vielen Zahlen zu erstellen, wie Sie möchten, und sie dann alle durch die Summe zu teilen. Sie sind auf diese Weise völlig zufällig.
r = [ran.random() for i in range(1,100)] s = sum(r) r = [ i/s for i in r ]
oder, wie von @TomKealy vorgeschlagen, halten Sie die Summe und die Erstellung in einer Schleife:
rs = [] s = 0 for i in range(100): r = ran.random() s += r rs.append(r)
Verwenden Sie für die schnellste Leistung
numpy
:import numpy as np a = np.random.random(100) a /= a.sum()
Und Sie können den Zufallszahlen jede gewünschte Verteilung für eine Wahrscheinlichkeitsverteilung geben:
a = np.random.normal(size=100) a /= a.sum()
---- Zeitliche Koordinierung ----
In [52]: %%timeit ...: r = [ran.random() for i in range(1,100)] ...: s = sum(r) ...: r = [ i/s for i in r ] ....: 1000 loops, best of 3: 231 µs per loop In [53]: %%timeit ....: rs = [] ....: s = 0 ....: for i in range(100): ....: r = ran.random() ....: s += r ....: rs.append(r) ....: 10000 loops, best of 3: 39.9 µs per loop In [54]: %%timeit ....: a = np.random.random(100) ....: a /= a.sum() ....: 10000 loops, best of 3: 21.8 µs per loop
quelle
Wenn Sie jede Zahl durch die Summe dividieren, erhalten Sie möglicherweise nicht die gewünschte Verteilung. Beispielsweise wählt bei zwei Zahlen das Paar x, y = random.random (), random.random () einen Punkt gleichmäßig auf dem Quadrat 0 <= x <1, 0 <= y <1 aus. Teilen durch die Summe "projiziert" diesen Punkt (x, y) auf die Linie x + y = 1 entlang der Linie von (x, y) zum Ursprung. Punkte in der Nähe von (0,5,0,5) sind viel wahrscheinlicher als Punkte in der Nähe von (0,1,0,9).
Für zwei Variablen ergibt x = random.random (), y = 1-x eine gleichmäßige Verteilung entlang des geometrischen Liniensegments.
Mit 3 Variablen wählen Sie einen zufälligen Punkt in einem Würfel aus und projizieren (radial durch den Ursprung), aber Punkte in der Nähe der Mitte des Dreiecks sind wahrscheinlicher als Punkte in der Nähe der Eckpunkte. Die resultierenden Punkte befinden sich auf einem Dreieck in der x + y + z-Ebene. Wenn Sie eine unvoreingenommene Auswahl von Punkten in diesem Dreieck benötigen, ist die Skalierung nicht gut.
Das Problem wird in n-Dimensionen kompliziert, aber Sie können eine Schätzung mit geringer Genauigkeit (aber hoher Genauigkeit für alle Laborwissenschaftler!) Erhalten, indem Sie einheitlich aus der Menge aller n-Tupel nicht negativer Ganzzahlen auswählen, die sich zu summieren N, und dann teilen Sie jeden von ihnen durch N.
Ich habe kürzlich einen Algorithmus entwickelt, um dies für n, N mit bescheidener Größe zu tun. Er sollte für n = 100 und N = 1.000.000 funktionieren, um 6-stellige Zufälle zu erhalten. Siehe meine Antwort unter:
Eingeschränkte Zufallszahlen erstellen?
quelle
Erstellen Sie eine Liste bestehend aus 0 und 1 und fügen Sie dann 99 Zufallszahlen hinzu. Sortieren Sie die Liste. Aufeinanderfolgende Unterschiede sind die Intervalllängen, die sich zu 1 addieren.
Ich spreche Python nicht fließend, also vergib mir, wenn es einen pythonischeren Weg gibt, dies zu tun. Ich hoffe, die Absicht ist klar:
import random values = [0.0, 1.0] for i in range(99): values.append(random.random()) values.sort() results = [] for i in range(1,101): results.append(values[i] - values[i-1]) print results
Hier ist eine aktualisierte Implementierung in Python 3:
import random def sum_to_one(n): values = [0.0, 1.0] + [random.random() for _ in range(n - 1)] values.sort() return [values[i+1] - values[i] for i in range(n)] print(sum_to_one(100))
quelle
Zusätzlich zur Lösung von @ pjs können wir auch eine Funktion mit zwei Parametern definieren.
import numpy as np def sum_to_x(n, x): values = [0.0, x] + list(np.random.uniform(low=0.0,high=x,size=n-1)) values.sort() return [values[i+1] - values[i] for i in range(n)] sum_to_x(10, 0.6) Out: [0.079058655684546, 0.04168649034779022, 0.09897491411670578, 0.065152293196646, 0.000544800901222664, 0.12329662037166766, 0.09562168167787738, 0.01641359261155284, 0.058273232428072474, 0.020977718663918954]
quelle
Generieren Sie 100 Zufallszahlen, egal in welchem Bereich. Summiere die generierten Zahlen, dividiere jedes Individuum durch die Summe.
quelle
Wenn Sie einen Mindestschwellenwert für die zufällig ausgewählten Zahlen haben möchten (dh die generierten Zahlen sollten mindestens sein
min_thresh
),rand_prop = 1 - num_of_values * min_thresh random_numbers = (np.random.dirichlet(np.ones(10),size=1)[0] * rand_prop) + min_thresh
Stellen Sie einfach sicher, dass Sie num_of_values (Anzahl der zu generierenden Werte) haben, damit die erforderlichen Zahlen generiert werden können (
num_values <= 1/min_thesh
)Im Grunde genommen legen wir einen Teil von 1 für den Mindestschwellenwert fest und erstellen dann Zufallszahlen in einem anderen Teil. Wir fügen hinzu
min_thesh
zu allen Zahlen, um die Summe 1 zu erhalten. Zum Beispiel: Nehmen wir an, Sie möchten 3 Zahlen mit min_thresh = 0,2 generieren. Wir erstellen einen Teil, der durch Zufallszahlen gefüllt werden soll [1 - (0,2x3) = 0,4]. Wir füllen diesen Teil und addieren 0,2 zu allen Werten, damit wir auch 0,6 füllen können.Dies ist eine Standardskalierung und -verschiebung, die in der Theorie der Zufallszahlengenerierung verwendet wird. Der Kredit geht an meinen Freund Jeel Vaishnav (ich bin nicht sicher, ob er ein SO-Profil hat) und an @sega_sai.
quelle
Sie könnten leicht tun mit:
r.append(1 - sum(r))
quelle
N-1
Zahlen korreliert .Im Sinne von "Teilen Sie jedes Element in der Liste durch die Summe der Liste" erstellt diese Definition eine Liste von Zufallszahlen mit der Länge = PARTS, sum = TOTAL, wobei jedes Element auf PLACES (oder None) gerundet wird:
import random import time PARTS = 5 TOTAL = 10 PLACES = 3 def random_sum_split(parts, total, places): a = [] for n in range(parts): a.append(random.random()) b = sum(a) c = [x/b for x in a] d = sum(c) e = c if places != None: e = [round(x*total, places) for x in c] f = e[-(parts-1):] g = total - sum(f) if places != None: g = round(g, places) f.insert(0, g) log(a) log(b) log(c) log(d) log(e) log(f) log(g) return f def tick(): if info.tick == 1: start = time.time() alpha = random_sum_split(PARTS, TOTAL, PLACES) log('********************') log('***** RESULTS ******') log('alpha: %s' % alpha) log('total: %.7f' % sum(alpha)) log('parts: %s' % PARTS) log('places: %s' % PLACES) end = time.time() log('elapsed: %.7f' % (end-start))
Ergebnis:
Waiting... Saved successfully. [2014-06-13 00:01:00] [0.33561018369775897, 0.4904215932650632, 0.20264927800402832, 0.118862130636748, 0.03107818050878819] [2014-06-13 00:01:00] 1.17862136611 [2014-06-13 00:01:00] [0.28474809073311597, 0.41609766067850096, 0.17193755673414868, 0.10084844382959707, 0.02636824802463724] [2014-06-13 00:01:00] 1.0 [2014-06-13 00:01:00] [2.847, 4.161, 1.719, 1.008, 0.264] [2014-06-13 00:01:00] [2.848, 4.161, 1.719, 1.008, 0.264] [2014-06-13 00:01:00] 2.848 [2014-06-13 00:01:00] ******************** [2014-06-13 00:01:00] ***** RESULTS ****** [2014-06-13 00:01:00] alpha: [2.848, 4.161, 1.719, 1.008, 0.264] [2014-06-13 00:01:00] total: 10.0000000 [2014-06-13 00:01:00] parts: 5 [2014-06-13 00:01:00] places: 3 [2014-06-13 00:01:00] elapsed: 0.0054131
quelle
Im Geiste der Methode von pjs:
a = [0, total] + [random.random()*total for i in range(parts-1)] a.sort() b = [(a[i] - a[i-1]) for i in range(1, (parts+1))]
Wenn Sie möchten, dass sie auf Dezimalstellen gerundet werden:
if places == None: return b else: b.pop() c = [round(x, places) for x in b] c.append(round(total-sum(c), places)) return c
quelle