создание неравномерного целочисленного распределения с использованием tr1 <random> - PullRequest
8 голосов
/ 14 марта 2011

Прямо сейчас я использую следующий код для создания равномерного распределения целых чисел с диапазоном. (Я вынул код посева)

int random(int min, int max)
{
    static std::mt19937 gen;
    std::uniform_int<int> dist(min, max);
    return dist(gen);

}

Я пытаюсь изменить его, чтобы получить распределение, которое предпочитает минимальное значение и почти никогда не дает значения, близкое к максимальному. Я вижу все готовые дистрибутивы, но ни один из них не является целым числом. А также я не могу сказать, какой из них соответствует моим потребностям, основываясь на любой документации. Самое близкое, что я получил, - это распределение хи-квадрат, как показано в Википедии, где k = 2

image

Но я не могу понять, основываясь на документации , как использовать ее с целыми числами, не говоря уже о том, чтобы установить значение k.

Как я могу настроить свою функцию на использование соответствующего неравномерного целочисленного распределения?


все еще работает над выбором правильного дистрибутива: вот результаты std::poisson_distribution<int> dist((max - min) * .1); от 0 до 20: image

пока не совсем, так как 0 должно быть чаще, чем 1, но это должно помочь следующему человеку опубликовать больше результатов по мере их поступления.


хорошо, мое окончательное решение стало комбинацией методов:

int randomDist(int min, int max)
{
    static std::mt19937 gen;
    std::chi_squared_distribution<double> dist(2);

    int x;
    do
    {
    x = (int)(max*dist(gen)/10) + min;
    }
    while (x > max);
    return x;
}

дает результат:

image

Ответы [ 2 ]

9 голосов
/ 14 марта 2011

Там есть другие целочисленные дистрибутивы, но в их именах просто нет int. Они имеют typedef IntType result_type в своих определениях классов.

Те, которые ведут себя так, как вы описываете:

  • binomial_distribution(t, p)

    Это генерирует числа в диапазоне 0 ≤ x t , поэтому вам нужно перевести диапазон на min. Среднее значение составляет t · p , поэтому выберите p возле 0.

    std::binomial_distribution<int> dist(max - min, .1);
    return dist(gen) + min;

  • poisson_distribution(λ)

    Это генерирует числа 0 ≤ x <∞, но большие числа постепенно становятся менее вероятными. Вы можете подвергнуть цензуре все, что выше <code>max, чтобы ограничить его диапазоном. Параметр λ является средним значением. Выберите его в соответствии с предыдущим примером:

    std::poisson_distribution<int> dist((max - min) * .1);
    int x;
    do
        x = dist(gen) + min;
    while (x > max);
    return x;

  • geometric_distribution(p)

    Также генерирует числа 0 ≤ x <∞, но 0 - наиболее вероятный результат, и каждое последующее число менее вероятно, чем предыдущее. Снова выбираем параметр, соответствующий среднему значению предыдущего примера: </p>

    std::geometric_distribution<int> dist(1 / ((max - min) * .1 + 1));
    int x;
    do
        x = dist(gen) + min;
    while (x > max);
    return x;

Вы также можете использовать любое из непрерывных распределений, чтобы сгенерировать double, а затем округлить его до int.

6 голосов
/ 14 марта 2011

В дополнение к распределениям, указанным в замечательном ответе @ aaz , имейте в виду, что вы также можете преобразовать ваше равномерное распределение в любую функцию распределения вероятностей, о которой вы только можете подумать, используя обратное преобразованиевыборка (что на самом деле возможно только для некоторых "хороших" функций) или выборка отклонения (может применяться в любом случае, но может быть вычислительно дорогой).

Кажетсямне, что распределение, которое соответствовало бы вашим потребностям, было бы (отрицательным) экспоненциальным распределением :

Exponential distribution

К счастью, это одно из распределений, к которымВы можете применить выборку обратного преобразования, что означает, что, имея выборку из равномерного распределения [0, 1], вы можете получить экспоненциальное распределение, применив формулу:

x = - ln(1-p)/lambda

с p - этослучайное значение из равномерного распределения и lambda - параметр экспоненциального распределения;см. здесь для получения дополнительной информации.

Как только вы получите x (который будет double), просто приведите его к int (или округлите его с помощью функции вроде:

int round(double val)
{
    // warning: can give counterintuitive results with negative numbers
    return int(val+0.5);
}

) для получения вашего результата.

Редактировать

Кстати, я не заметил, что даже экспоненциальное распределение уже включено в <random> ( ссылка * 1041)*) ... ну, еще лучше, вам не нужно писать код, но немного теории никогда не теряется :).

...