Подтвердить что ты не робот

Создание списка случайных чисел, суммирование до 1

Как бы я сделал список N (скажем, 100) случайных чисел, так что их сумма равна 1?

Я могу составить список случайных чисел с

r = [ran.random() for i in range(1,100)]

Как мне изменить это, чтобы список суммировался до 1 (это для моделирования вероятности).

4b9b3361

Ответ 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