Получение N случайных чисел с суммой M

Я хочу получить N случайных чисел, чья сумма является значением.

Например, предположим, я хочу 5 случайных чисел, сумма которых равна 1.

Тогда действительная возможность:

0.2 0.2 0.2 0.2 0.2

Другая возможность:

0.8 0.1 0.03 0.03 0.04

И так далее. Мне это нужно для создания матрицы вещей для нечетких C-средних.

Ответ 1

Короткий ответ:

Просто сгенерируйте N случайных чисел, вычислите их сумму, разделите каждое на сумму и умножьте на M.

Более длинный ответ:

Приведенное выше решение не дает равномерного распределения, которое может быть проблемой в зависимости от того, для чего используются эти случайные числа. Другой метод, предложенный Матти Вирккуненом:

Сгенерируйте N-1 случайных чисел от 0 до 1, добавьте в список сами числа 0 и 1, отсортируйте их и возьмите различия соседних чисел.

Я не уверен, что это даст равномерное распределение

Ответ 2

Генерировать случайные числа N-1 между 0 и 1, добавлять числа 0 и 1 в список, сортировать их и принимать различия соседних чисел.

Ответ 3

Я думаю, что стоит отметить, что принятый в настоящее время ответ не дает равномерного распределения:

"Просто сгенерируйте N случайных чисел, вычислите их сумму, разделите их на сумма"

Чтобы увидеть это, рассмотрим случай N = 2 и M = 1. Это тривиальный случай, так как мы можем сгенерировать список [x, 1-x], выбирая x равномерно в диапазоне (0,1). Предлагаемое решение порождает пару [x/(x + y), y/(x + y)], где x и y равномерны в (0,1). Чтобы проанализировать это, выберем некоторое г такое, что 0 < z < 0,5 и вычислить вероятность того, что первый элемент меньше z. Эта проблема должна быть равна z, если распределение равномерно. Однако мы получаем

Prob (x/(x + y) < z) = Prob (x < z (x + y)) = Prob (x (1-z) < zy) = Prob (x < y ( z/(1-z))) = z/(2-2z).

Я сделал несколько быстрых вычислений, и кажется, что единственное решение, которое пока что привело к равномерному распределению, было предложенное Матти Вирккуненом:

"Генерировать случайные числа N-1 между 0 и 1, добавлять числа 0 и 1 в список, сортировать их и брать различия соседних чисел".

Ответ 4

В Java:

private static double[] randSum(int n, double m) {
    Random rand = new Random();
    double randNums[] = new double[n], sum = 0;

    for (int i = 0; i < randNums.length; i++) {
        randNums[i] = rand.nextDouble();
        sum += randNums[i];
    }

    for (int i = 0; i < randNums.length; i++) {
        randNums[i] /= sum * m;
    }

    return randNums;
}

Ответ 5

Просто сгенерируйте N случайных чисел, вычислите их сумму, разделите каждое на сумму.

Развертывая принятый ответ Гийома, здесь Java-функция, которая делает именно это.

public static double[] getRandDistArray(int n, double m)
{
    double randArray[] = new double[n];
    double sum = 0;

    // Generate n random numbers
    for (int i = 0; i < randArray.length; i++)
    {
        randArray[i] = Math.random();
        sum += randArray[i];
    }

    // Normalize sum to m
    for (int i = 0; i < randArray.length; i++)
    {
        randArray[i] /= sum;
        randArray[i] *= m;
    }
    return randArray;
}

В тестовом прогоне getRandDistArray(5, 1.0) вернул следующее:

[0.38106150346121903, 0.18099632814238079, 0.17275044310377025, 0.01732932296660358, 0.24786240232602647]

Ответ 6

Эта проблема эквивалентна задаче генерации случайных чисел с Распределение Дирихле. Для генерации N положительных чисел, которые суммируются с положительным числом M, где каждая возможная комбинация одинаково вероятна:

  • Генерировать N экспоненциально распределенных случайных чисел. Один из способов генерации такого числа можно записать как —

    number = -ln(1.0 - RNDU())
    

    где ln(x) - естественный логарифм x, а RNDU() - метод, который возвращает случайное число 0 или больше и меньше 1 (например, JavaScript Math.random()). Обратите внимание, что генерация этих чисел с равномерным распределением не является идеальной, поскольку приведет к смещенному распределению комбинаций случайных чисел.

  • Разделите числа, сгенерированные таким образом по их сумме.
  • Умножьте каждое число на M.

В результате получается N чисел в распределении Дирихле, сумма которых приблизительно равна M (я говорю "приблизительно" из-за ошибки округления).

Эта проблема также эквивалентна задаче генерации случайных чисел равномерно из N-мерного симплекса.

Ответ 7

  • Создание случайных чисел N-1.
  • Вычислить сумму указанных чисел.
  • Добавьте разницу между вычисленной суммой и желаемой суммой к набору.

Теперь у вас есть N случайных чисел, и их сумма - это желаемая сумма.

Ответ 8

К сожалению, некоторые ответы здесь неверны, если вы хотите получить равномерно случайные числа. Самое простое (и самое быстрое во многих языках) решение, которое гарантирует равномерно случайные числа, это просто

# This is Python, but most languages support the Dirichlet.
import numpy as np
np.random.dirichlet(np.ones(n))*m

где n - количество случайных чисел, которые вы хотите сгенерировать, а m - сумма результирующего массива. Этот подход дает положительные значения и особенно полезен для генерации достоверных вероятностей, которые составляют 1 (пусть m = 1).

Ответ 9

Вы немного ограничены ограничениями. Много и много процедур будут работать.

Например, обычно ли номера распределены? Равномерное?
Я полагаю, что все числа должны быть положительными и равномерно распределены вокруг среднего значения, M/N.

Попробуйте это.

  • mean = M/N.
  • Генерировать значения N-1 между 0 и 2 *. Это может быть стандартное число от 0 до 1, u, а случайное значение - (2 * u-1) * означает создание значения в соответствующем диапазоне.
  • Вычислить сумму значений N-1.
  • Оставшееся значение - N-сумма.
  • Если оставшееся значение не соответствует ограничениям (от 0 до 2 *), повторите процедуру.

Ответ 10

Найдите случайное число между 0 и общим количеством, которое вы хотите. Вычтите число из общего числа. Повторите n-1 раз. Конечная сумма - это окончательное число.

List<Double> result= new ArrayList<Double>();
double total = 1.0;
for (int i = 0;++i < 5;) {
    double db = Math.random() * total;
    result.add( db );
    total -= db;
}
result.add( total );