Без предвзятости верните список n случайных положительных чисел ( >= 0), чтобы их сумма == total_sum
Я либо ищу алгоритм, либо предложение улучшить свой код для создания списка случайных чисел, что их сумма равна произвольному числу. С моим кодом ниже, он всегда будет предвзятым, поскольку первые числа будут иметь тенденцию быть выше.
Есть ли способ сделать выбор числа более эффективным?
#!/usr/bin/python
'''
Generate a list of 'numbs' positive random numbers whose sum = 'limit_sum'
'''
import random
def gen_list(numbs, limit_sum):
my_sum = []
for index in range(0, numbs):
if index == numbs - 1:
my_sum.append(limit_sum - sum(my_sum))
else:
my_sum.append(random.uniform(0, limit_sum - sum(my_sum)))
return my_sum
#test
import pprint
pprint.pprint(gen_list(5, 20))
pprint.pprint(gen_list(10, 200))
pprint.pprint(gen_list(0, 30))
pprint.pprint(gen_list(1, 10))
ВЫХОД
## output
[0.10845093828525609,
16.324799712999706,
0.08200162072303821,
3.4534885160590041,
0.031259211932997744]
[133.19609626532952,
47.464880208741029,
8.556082341110228,
5.7817325913462323,
4.6342577008233716,
0.22532341156764768,
0.0027495225618908918,
0.064738336208217895,
0.028888697891734455,
0.045250924420116689]
[]
[10]
Ответы
Ответ 1
Хорошо, мы будем решать проблему, предполагая, что требование состоит в том, чтобы создать случайный вектор длины N, который равномерно распределен над допустимым пространством, пересчитывается следующим образом:
Учитывая
- желаемая длина L,
- желаемая общая сумма S,
- диапазон допустимых значений [0, B] для каждого скалярного значения,
порождают случайный вектор V длины N такой, что случайная величина V равномерно распределена по всему разрешенному пространству.
Мы можем упростить задачу, заметив, что мы можем вычислить V = U * S, где U - подобный случайный вектор с нужной суммой 1 и диапазон допустимых значений [0, b], где b = B/S. Значение b должно быть между 1/N и 1.
Сначала рассмотрим N = 3. Пространство допустимых значений {U} является частью плоскости, перпендикулярной вектору [1 1 1], которая проходит через точку [1/3 1/3 1/3] и которая лежит внутри куба, компоненты которого находятся в диапазоне от 0 до b. Этот набор точек {U} имеет форму шестиугольника.
(TBD: picture. Я не могу создать его прямо сейчас, мне нужен доступ к MATLAB или другой программе, которая может делать 3D-графики. Моя установка Octave не может.)
Лучше всего использовать ортонормированную взвешивающую матрицу W (см. мой другой ответ) с одним вектором = [1 1 1]/sqrt (3). Одна такая матрица
octave-3.2.3:1> A=1/sqrt(3)
A = 0.57735
octave-3.2.3:2> K=1/sqrt(3)/(sqrt(3)-1)
K = 0.78868
octave-3.2.3:3> W = [A A A; A 1-K -K; A -K 1-K]
W =
0.57735 0.57735 0.57735
0.57735 0.21132 -0.78868
0.57735 -0.78868 0.21132
которая, опять же, ортонормирована (W * W = I)
Если вы рассматриваете точки куба [0 0 b], [0bb], [0 b 0], [bb 0], [b 0 0] и [b 0 b], они образуют шестиугольник и все это расстояние b * sqrt (2/3) от диагонали куба. Они не удовлетворяют рассматриваемой проблеме, но полезны через минуту. Остальные две точки [0 0 0] и [b b b] находятся на диагонали куба.
Ортонормальная взвешивающая матрица W позволяет нам создавать точки, равномерно распределенные внутри {U}, так как ортонормированные матрицы являются преобразованиями координат, которые вращаются/отражаются и не масштабируются или не искажаются.
Мы будем генерировать точки, равномерно распределенные в системе координат, определяемой 3 векторами W. Первая компонента - ось диагонали куба. Сумма U-компонент полностью зависит от этой оси и вовсе не от остальных. Поэтому координата вдоль этой оси должна быть 1/sqrt (3), которая соответствует точке [1/3, 1/3, 1/3].
Остальные два компонента находятся в направлениях, перпендикулярных диагонали куба. Поскольку максимальное расстояние от диагонали составляет b * sqrt (2/3), мы будем генерировать равномерно распределенные числа (u, v) между -b * sqrt (2/3) и + b * sqrt (2/3).
Это дает нам случайную величину U '= [1/sqrt (3) u v]. Затем мы вычисляем U = U '* W. Некоторые из результирующих точек будут за пределами допустимого диапазона (каждая компонента U должна находиться между 0 и b), и в этом случае мы отвергаем это и начинаем.
Другими словами:
- Генерировать независимые случайные переменные u и v, каждый из которых равномерно распределен между -b * sqrt (2/3) и + b * sqrt (3).
- Вычислить вектор U '= [1/sqrt (3) u v]
- Вычислить U = U '* W.
- Если какая-либо из U-компонент находится вне диапазона [0, b], отклоните это значение и вернитесь к шагу 1.
- Вычислить V = U * S.
Решение аналогично для более высоких размеров (равномерно распределенных точек внутри части гиперплоскости, перпендикулярной главной диагонали гиперкуба):
Предварительно рассчитайте весовую матрицу W ранга N.
- Генерировать независимые случайные величины u 1, u 2,... u N-1, каждый из которых равномерно распределен между -b * k ( N) и + b * k (N).
- Вычислить вектор U '= [1/N u 1, u 2,... u N-1]
- Вычислить U = U '* W. (есть ярлыки для фактического создания и умножения на W.)
- Если какая-либо из U-компонент находится вне диапазона [0, b], отклоните это значение и вернитесь к шагу 1.
- Вычислить V = U * S.
Диапазон k (N) является функцией от N, которая представляет максимальное расстояние вершин гиперкуба стороны 1 от его главной диагонали. Я не уверен в общей формуле, но это sqrt (2/3) для N = 3, sqrt (6/5) для N = 5, там, вероятно, есть для нее формула.
Ответ 2
Почему бы не просто сгенерировать правильное число равномерно распределенных случайных чисел, поднять их и масштабировать?
EDIT: Чтобы быть немного понятнее: вы хотите, чтобы N чисел суммировались с S? Поэтому создайте N равномерно распределенных случайных чисел на интервале [0,1) или независимо от того, что производит ваш RNG. Добавьте их, они будут суммировать s (скажем), тогда как вы хотите, чтобы они суммировали S, поэтому умножайте каждое число на S/s. Теперь числа равномерно распределены случайным образом на [0, S/s), я думаю.
Ответ 3
Вот как бы я это сделал:
- Создание n-1 случайных чисел, все в диапазоне [0,
max
]
- Сортировка этих чисел
- Для каждой пары, состоящей из i-го и (i + 1) -ного числа в отсортированном списке, создайте интервал (i, я + 1) и вычислите его длину. Последний интервал начинается с последнего номера и заканчивается на
max
, и первый интервал начинается с 0 и заканчивается первым номером в списке.
Теперь длины этих интервалов всегда будут суммироваться до max
, так как они просто представляют сегменты внутри [0, max
].
Код (в Python):
#! /usr/bin/env python
import random
def random_numbers(n,sum_to):
values=[0]+[random.randint(0,sum_to) for i in xrange(n-1)]+[sum_to]
values.sort()
intervals=[values[i+1]-values[i] for i in xrange(len(values)-1)]
return intervals
if __name__=='__main__':
print random_numbers(5,100)
Ответ 4
Если вы ищете нормально распределенные номера с минимальной корреляцией и должны быть строгими * об этом, я бы предложил вам воспользоваться следующим математическим подходом и перевести код.
(* rigorous: проблема с другими подходами заключается в том, что вы можете получить "длинные хвосты" в своих дистрибутивах - другими словами, это редко, но возможно иметь выбросы, которые сильно отличаются от ожидаемого результата)
- Генерировать N-1 независимые и идентично распределенные (IID) гауссовские случайные величины v 0, v 1, v 2,... v N-1, чтобы соответствовать степеням свободы вашей проблемы N-1.
- Создайте вектор-столбец V, где V = [0 v 0, v 1, v 2,... v N-1суб > ] T
- Используйте фиксированную взвешивающую матрицу W, где W состоит из ортонормированной матрицы **, верхняя строка которой [1 1 1 1 1 1 1... 1]/sqrt (N).
- Ваш выходной вектор является произведением WV + SU/N, где S - искомая сумма, а U - вектор столбца 1. Другими словами, i-я выходная переменная = точечное произведение (строка #i матрицы W) и вектор-столбец V, добавленная к S/N.
Стандартное отклонение каждой выходной переменной будет (я считаю, не могу проверить прямо сейчас) sqrt (N/N-1) * стандартное отклонение входных случайных величин.
** ортонормированная матрица: это трудная часть, я ставлю вопрос на math.stackexchange.com и там простую матрицу W, которая работает, и может определяться алгоритмически только с тремя различными значениями, поэтому вам фактически не нужно создавать матрицу.
W является отражателем домохозяйства vw, где v = [sqrt (N), 0, 0, 0,...] и w = [1 1 1 1 1... 1] может быть определено:
W(1,i) = W(i,1) = 1/sqrt(N)
W(i,i) = 1 - K for i >= 2
W(i,j) = -K for i,j >= 2, i != j
K = 1/sqrt(N)/(sqrt(N)-1)
Проблема с подходом Mark:
Почему бы не просто сгенерировать правильное число равномерно распределенных случайных чисел, поднять их и масштабировать?
заключается в том, что если вы это сделаете, вы получите "длинный хвост". Вот пример в MATLAB:
>> X = rand(100000,10);
>> Y = X ./ repmat(sum(X,2),1,10);
>> plot(sort(Y))
Я создал 100 000 наборов N = 10 чисел в матрице X и создал матрицу Y, где каждая строка Y является соответствующей строкой X, деленной на ее сумму (так что каждая строка Y суммируется до 1.0)
Вычисление отсортированных значений Y (каждый столбец, отсортированный отдельно) дает примерно такое же кумулятивное распределение:
Истинное равномерное распределение даст прямую линию от 0 до максимального значения. Вы заметите, что это немного похоже на истинное равномерное распределение, кроме как в конце, где есть длинный хвост. Там избыток чисел генерируется от 0,2 до 0,5. Хвост ухудшается при больших значениях N, потому что хотя среднее значение чисел уменьшается (среднее value = 1/N), максимальное значение остается равным 1.0: допустим вектор, состоящий из 9 значений 0.0 и 1 значения 1.0. и может быть сгенерирован таким образом, но патологически редок.
Если вам все равно, продолжайте использовать этот метод. Вероятно, есть способы генерации "почти" -образных или "почти" -уровских распределений с требуемыми суммами, которые намного проще и эффективнее, чем описанные выше. Но я предостерегаю вас, чтобы вы были осторожны и понимали последствия выбранного вами алгоритма.
Одно исправление, которое оставляет вещи разнородно распределенными без длинного хвоста, выглядит следующим образом:
- Создать вектор V = N равномерно распределенных случайных чисел от 0.0 до 1.0.
- Найдите их сумму S и максимальное значение M.
- Если S < k * M (максимальное значение слишком велико), вернитесь к шагу 1. Я не уверен, какое значение использовать для k, возможно k = N/2?
- Вывести вектор V * S желаемый/S
Пример в MATLAB для N = 10:
>> X = rand(100000,10);
>> Y = X ./ repmat(sum(X,2),1,10);
>> i = sum(X,2)>(10/2)*max(X,[],2);
>> plot(sort(Y(i,:)))
Ответ 5
Следующее довольно просто и возвращает равномерные результаты:
def gen_list(numbs, limit_sum):
limits = sorted([random.uniform(0, limit_sum) for _ in xrange(numbs-1)])
limits = [0] + limits + [limit_sum]
return [x1-x0 for (x0, x1) in zip(limits[:-1], limits[1:])]
Идея состоит в том, что если вам нужно, скажем, 5 чисел между 0 и 20, вы можете просто поставить 4 "пределы" между 0 и 20, и вы получите раздел (0, 20) интервал. Случайные числа, которые вы хотите, представляют собой просто длины из 5 интервалов в отсортированном списке [0, random1, random2, random3, random4, 20].
PS: упс! выглядит так же, как и MAK-ответ, хотя и закодирован без использования индексов!
Ответ 6
Я столкнулся с этой проблемой и, в частности, нуждался в целых числах. Ответ заключается в использовании многочлена.
import numpy.random, numpy
total_sum = 20
n = 6
v = numpy.random.multinomial(total_sum, numpy.ones(n)/n)
Как поясняет многокомпонентная документация, вы свернули справедливую шестистороннюю кость двадцать раз. v
содержит шесть чисел, указывающих количество раз, когда каждая сторона кости была поднята. Естественно, элементы v
должны суммироваться до двадцати. Здесь шесть равно n
, а двадцать - total_sum
.
С многочленом вы можете имитировать и несправедливые кости, что очень полезно в некоторых случаях.
Ответ 7
Вы можете сохранить текущую сумму, а не называть sum(my_sum)
несколько раз.