Создание списка случайных чисел, суммирование до 1
Как бы я сделал список N (скажем, 100) случайных чисел, так что их сумма равна 1?
Я могу составить список случайных чисел с
r = [ran.random() for i in range(1,100)]
Как мне изменить это, чтобы список суммировался до 1 (это для моделирования вероятности).
Ответы
Ответ 1
Самое простое решение - действительно взять N случайных значений и делить на сумму.
Более общим решением является использование распределения Дирихле
http://en.wikipedia.org/wiki/Dirichlet_distribution
который доступен в numpy.
Изменяя параметры распределения, вы можете изменить "случайность" отдельных чисел
>>> 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]]
В зависимости от основного параметра распределение Дирихле будет либо давать векторы, где все значения близки к 1./N, где N - длина вектора, или дают векторы, где большинство значений векторов будет ~ 0, и будет один сингл 1 или дать что-то между этими возможностями.
Ответ 2
Лучший способ сделать это - просто составить список из числа чисел, сколько пожелаете, а затем разделить их на сумму. Они полностью случайны таким образом.
r = [ran.random() for i in range(1,100)]
s = sum(r)
r = [ i/s for i in r ]
или, как было предложено @TomKealy, сохранить сумму и создание в одном цикле:
rs = []
s = 0
for i in range(100):
r = ran.random()
s += r
rs.append(r)
Для достижения максимальной производительности используйте numpy
:
import numpy as np
a = np.random.random(100)
a /= a.sum()
И вы можете дать случайным числам любое распределение, которое вы хотите, для распределения вероятности:
a = np.random.normal(size=100)
a /= a.sum()
---- Сроки ----
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
Ответ 3
Разделение каждого номера на общую сумму может не дать вам распределение, которое вы хотите. Например, с двумя числами пара x, y = random.random(), random.random() выбирает точку равномерно на квадрате 0 <= x < 1, 0 <= y < 1. Разделение на сумму "проектов" точки (x, y) на прямую x + y = 1 вдоль линии от (x, y) до начала координат. Точки вблизи (0,5,0,5) будут гораздо более вероятными, чем точки вблизи (0,1,0,9).
Для двух переменных х = random.random(), y = 1-x дает равномерное распределение вдоль сегмента геометрической линии.
С тремя переменными вы выбираете случайную точку в кубе и проецируете (радиально, через начало координат), но точки вблизи центра треугольника будут более вероятными, чем точки вблизи вершин. Полученные точки находятся на треугольнике в плоскости x + y + z. Если вам нужен несмещенный выбор точек в этом треугольнике, масштабирование не является хорошим.
Проблема усложняется в n-измерениях, но вы можете получить низкоточную (но высокую точность, для всех поклонников лабораторных наук!), выбирая равномерно из набора всех n-наборов неотрицательных целых чисел сложение до N, а затем деление каждого из них на N.
Недавно я придумал алгоритм для этого для n, N. для скромного размера. Он должен работать для n = 100 и N = 1,000,000, чтобы дать вам 6-значные рандомы. См. Мой ответ по адресу:
Создать ограниченные случайные числа?
Ответ 4
Создайте список, состоящий из 0 и 1, затем добавьте 99 случайных чисел. Сортировка списка. Последовательными различиями будут длины интервалов, которые составляют до 1.
Я не свободно говорю на Python, так что простите меня, если есть более питонический способ сделать это. Надеюсь, что цель понятна:
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
Ответ 5
генерирует 100 случайных чисел, не имеет значения, какой диапазон.
суммировать числа, сгенерированные, делить каждого индивида на общую сумму.
Ответ 6
Вы можете легко сделать:
r.append(1 - sum(r))
Ответ 7
В духе "разделить каждый элемент списка по сумме списка" это определение создаст список случайных чисел длины = PARTS, sum = TOTAL, причем каждый элемент округлен до PLACES (или None):
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))
результат:
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
Ответ 8
В духе метода 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))]
Если вы хотите, чтобы они округлились до десятичных знаков:
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