Jak sporządziłbym listę N (powiedzmy 100) liczb losowych, tak aby ich suma wynosiła 1?
Mogę utworzyć listę liczb losowych za pomocą
r = [ran.random() for i in range(1,100)]
Jak bym to zmodyfikował, aby lista sumowała się do 1 (to jest dla symulacji prawdopodobieństwa).
Odpowiedzi:
Najprostszym rozwiązaniem jest rzeczywiście wybranie N losowych wartości i podzielenie ich przez sumę.
Bardziej ogólnym rozwiązaniem jest użycie dystrybucji Dirichlet http://en.wikipedia.org/wiki/Dirichlet_distribution, która jest dostępna w numpy.
Zmieniając parametry rozkładu można zmienić „losowość” poszczególnych liczb
>>> 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]]
W zależności od głównego parametru rozkład Dirichleta da wektory, w których wszystkie wartości są bliskie 1 / N, gdzie N jest długością wektora, lub da wektorów, w których większość wartości wektorów będzie wynosić ~ 0, i tam będzie pojedynczym 1 lub da coś pomiędzy tymi możliwościami.
EDYCJA (5 lat po pierwotnej odpowiedzi): Kolejnym użytecznym faktem dotyczącym rozkładu Dirichleta jest to, że otrzymujesz go w naturalny sposób, jeśli wygenerujesz zestaw zmiennych losowych o rozkładzie Gamma, a następnie podzielisz je przez ich sumę.
źródło
[0,1/s)
). Będzie dokładnie tak jednolity, jak nieskalowana dystrybucja, od której zacząłeś, ponieważ skalowanie nie zmienia dystrybucji, a jedynie ją kompresuje. Ta odpowiedź daje różne rozkłady, z których tylko jeden jest jednolity. Jeśli to nie ma dla Ciebie sensu, uruchom przykłady i spójrz na niektóre histogramy, aby było jasne. Spróbuj również tego samego z rozkładem Gaussa (np.random.normal
).Najlepszym sposobem, aby to zrobić, jest po prostu sporządzenie listy dowolnej liczby liczb, a następnie podzielenie ich wszystkich przez sumę. W ten sposób są całkowicie przypadkowe.
r = [ran.random() for i in range(1,100)] s = sum(r) r = [ i/s for i in r ]
lub, jak sugeruje @TomKealy, utrzymuj sumę i tworzenie w jednej pętli:
rs = [] s = 0 for i in range(100): r = ran.random() s += r rs.append(r)
Aby uzyskać najszybszą wydajność, użyj
numpy
:import numpy as np a = np.random.random(100) a /= a.sum()
I możesz nadać liczbom losowym dowolny rozkład, który chcesz, aby uzyskać rozkład prawdopodobieństwa:
a = np.random.normal(size=100) a /= a.sum()
---- Wyczucie czasu ----
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
źródło
Dzielenie każdej liczby przez całość może nie dać pożądanego rozkładu. Na przykład przy dwóch liczbach para x, y = random.random (), random.random () wybiera punkt równomiernie na kwadracie 0 <= x <1, 0 <= y <1. Dzieląc przez sumę „rzutuje” ten punkt (x, y) na linię x + y = 1 wzdłuż linii od (x, y) do początku. Punkty w pobliżu (0,5,0,5) będą znacznie bardziej prawdopodobne niż punkty w pobliżu (0,1,0,9).
Zatem dla dwóch zmiennych x = random.random (), y = 1-x daje równomierny rozkład wzdłuż geometrycznego odcinka linii.
Mając 3 zmienne, wybierasz losowy punkt w sześcianie i rzutujesz (promieniowo, przez początek), ale punkty w pobliżu środka trójkąta będą bardziej prawdopodobne niż punkty w pobliżu wierzchołków. Wynikowe punkty znajdują się na trójkącie w płaszczyźnie x + y + z. Jeśli potrzebujesz obiektywnego wyboru punktów w tym trójkącie, skalowanie nie jest dobre.
Problem komplikuje się w n-wymiarach, ale można uzyskać niską precyzję (ale wysoką dokładność, dla wszystkich fanów nauk laboratoryjnych!), Wybierając jednolicie ze zbioru wszystkich n-krotek nieujemnych liczb całkowitych, które sumują się do N, a następnie podzielenie każdego z nich przez N.
Niedawno wymyśliłem algorytm, który robi to dla skromnych n, N. Powinien działać dla n = 100 i N = 1000000, aby dać ci 6-cyfrowe losy. Zobacz moją odpowiedź na:
Utworzyć ograniczone liczby losowe?
źródło
Utwórz listę składającą się z 0 i 1, a następnie dodaj 99 losowych liczb. Sortuj listę. Kolejne różnice będą długościami przedziałów, które sumują się do 1.
Nie mówię biegle w Pythonie, więc wybacz mi, jeśli istnieje bardziej Pythonowy sposób na zrobienie tego. Mam jednak nadzieję, że zamiar jest jasny:
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
Oto zaktualizowana implementacja w Pythonie 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))
źródło
Oprócz rozwiązania @ pjs możemy zdefiniować funkcję z dwoma parametrami.
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]
źródło
wygeneruj 100 liczb losowych, nie ma znaczenia w jakim zakresie. zsumuj wygenerowane liczby, podziel każdą osobę przez sumę.
źródło
W przypadku, gdy chcesz mieć minimalny próg dla losowo wybranych liczb (tj. Generowane liczby powinny być co najmniej
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
Po prostu upewnij się, że masz num_of_values (liczbę wartości do wygenerowania), aby można było wygenerować wymagane liczby (
num_values <= 1/min_thesh
)Zasadniczo ustalamy część 1 dla progu minimalnego, a następnie tworzymy liczby losowe w innej części. Dodajemy
min_thesh
do wszystkich liczb, aby otrzymać sumę 1. Na przykład: powiedzmy, że chcesz wygenerować 3 liczby, z min_thresh = 0.2. Tworzymy porcję do wypełnienia liczbami losowymi [1 - (0,2x3) = 0,4]. Wypełniamy tę porcję i dodajemy 0,2 do wszystkich wartości, więc możemy również wypełnić 0,6.Jest to standardowe skalowanie i przesuwanie używane w teorii generowania liczb losowych. Podziękowania należą się mojej przyjaciółce Jeel Vaishnav (nie jestem pewien, czy ma profil SO) i @sega_sai.
źródło
Możesz łatwo zrobić z:
r.append(1 - sum(r))
źródło
N-1
liczbami.W duchu „podziel każdy element na liście przez sumę listy”, definicja ta utworzy listę liczb losowych o długości = CZĘŚCI, suma = TOTAL, z każdym elementem zaokrąglonym do MIEJSC (lub Żaden):
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))
wynik:
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
źródło
W duchu metody 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))]
Jeśli chcesz, aby zostały zaokrąglone do miejsc dziesiętnych:
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
źródło