Numpy массив с заданными значениями и суммой, равной 1 - PullRequest
0 голосов
/ 29 апреля 2020

Я хочу создать случайный массив np.array со значениями, включающими только [0,05, 0,1, 0,15, ... 0,9, 0,95, 1] ​​с суммой значений = 1

Я знаю, как создать случайный массив, например, из 5 элементов, так что сумма равна 1:

array = np.random.random(5)
array /= np.sum(array)

Но как я могу сделать так, чтобы значения в этом массиве были только из [0,05, 0,1, 0,15, ... 0,9, 0,95, 1]?

Upd. Этот метод работает, но, возможно, есть и другие способы pythoni c. 100 массивов

sets = []
while len(sets) < 100:
    array = np.random.choice(np.arange(0, 1.05, 0.05), 5)
    if np.sum(array) == 1:
        sets.append(array)

1 Ответ

0 голосов
/ 29 апреля 2020

Ваша задача эквивалентна следующей задаче:

  1. Выберите 5 случайных чисел в [1, 20] с суммой 20, где целые числа появляются в случайном порядке.
  2. Разделите выбранные целые числа на 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)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...