Ваша задача эквивалентна следующей задаче:
- Выберите 5 случайных чисел в
[1, 20]
с суммой 20
, где целые числа появляются в случайном порядке. - Разделите выбранные целые числа на 20.
Код Python ниже показывает, как это можно реализовать. Он имеет следующие преимущества:
- Он не использует выборку отбраковки.
- Он выбирает случайным образом из всех комбинаций, которые отвечают требованиям, равномерно.
Он основан на алгоритме Джона Макклейна, который он опубликовал как ответ на другой вопрос . Я опишу алгоритм в другом ответе.
import random # Or secrets
def _getSolTable(n, mn, mx, sum):
t = [[0 for i in range(sum + 1)] for j in range(n + 1)]
t[0][0] = 1
for i in range(1, n + 1):
for j in range(0, sum + 1):
jm = max(j - (mx - mn), 0)
v = 0
for k in range(jm, j + 1):
v += t[i - 1][k]
t[i][j] = v
return t
def intsInRangeWithSum(numSamples, numPerSample, mn, mx, sum):
""" Generates one or more combinations of
'numPerSample' numbers each, where each
combination's numbers sum to 'sum' and are listed
in any order, and each
number is in the interval '[mn, mx]'.
The combinations are chosen uniformly at random.
'mn', 'mx', and
'sum' may not be negative. Returns an empty
list if 'numSamples' is zero.
The algorithm is thanks to a _Stack Overflow_
answer (`questions/61393463`) by John McClane.
Raises an error if there is no solution for the given
parameters. """
adjsum = sum - numPerSample * mn
# Min, max, sum negative
if mn < 0 or mx < 0 or sum < 0:
raise ValueError
# No solution
if numPerSample * mx < sum:
raise ValueError
if numPerSample * mn > sum:
raise ValueError
if numSamples == 0:
return []
# One solution
if numPerSample * mx == sum:
return [[mx for i in range(numPerSample)] for i in range(numSamples)]
if numPerSample * mn == sum:
return [[mn for i in range(numPerSample)] for i in range(numSamples)]
samples = [None for i in range(numSamples)]
table = _getSolTable(numPerSample, mn, mx, adjsum)
for sample in range(numSamples):
s = adjsum
ret = [0 for i in range(numPerSample)]
for ib in range(numPerSample):
i = numPerSample - 1 - ib
# Or secrets.randbelow(table[i + 1][s])
v = random.randint(0, table[i + 1][s] - 1)
r = mn
v -= table[i][s]
while v >= 0:
s -= 1
r += 1
v -= table[i][s]
ret[i] = r
samples[sample] = ret
return samples
Пример:
weights=intsInRangeWithSum(
# One sample
1,
# Count of numbers per sample
5,
# Range of the random numbers
1, 20,
# Sum of the numbers
20)
# Divide by 100 to get weights that sum to 1
weights=[x/20.0 for x in weights[0]]
В любом случае ваш код ниже:
array = np.random.random(5)
array /= np.sum(array)
... не приводит к равномерной случайной комбинации чисел с суммой 1. Вместо этого используйте следующий код вместо этого кода, отметив, что равномерное поступление экспоненциально с интервалом, а не с одинаковым интервалом , (Для ясности, это не решает проблему в вашем вопросе; это только наблюдение. Не берите в голову тот факт, что numpy.random.*
функции теперь унаследованные функции начиная с NumPy 1.17, частично потому, что они использовать глобальное состояние.)
array = np.random.exponential(5)
array /= np.sum(array)