У меня есть:

std::random_device rd;
std::mt19937 mt(rd());
std::uniform_int_distribution<int> probability(0, 100);

Я хочу исключить некоторые числа из этого диапазона вероятностей.
Пример 1: Допустим, я хочу сгенерировать случайное число от 0 до 100, но это число никогда не может быть 4.
Пример 2: Допустим, я хочу сгенерировать случайное число от 0 до 100, но это число никогда не может быть любым числом от 4 до 7.

Интересно, можно ли этого добиться в современном C ++ без использования std :: rand?
Заранее спасибо!

4
AxoyTO 14 Окт 2021 в 16:08

5 ответов

Лучший ответ

Если вы хотите остаться с uniform_int_distribution, вы можете сделать это вручную следующим образом:

Пример 1: Допустим, я хочу сгенерировать случайное число от 0 до 100, но это число никогда не может быть 4.

std::random_device rd;
std::mt19937 mt(rd());
std::uniform_int_distribution<int> distribution(0,99);
auto temp = distribution(mt);
auto random_number = (temp < 4) ? temp : temp + 1;

Пример 2: Допустим, я хочу сгенерировать случайное число от 0 до 100, но это число никогда не может быть любым числом от 4 до 7.

std::random_device rd;
std::mt19937 mt(rd());
std::uniform_int_distribution<int> distribution(0,96);
auto temp = distribution(mt);
auto random_number = (temp < 4) ? temp : temp + 4;

Это можно было бы обобщить, чтобы написать функцию random_int_between_excluding(int first, int last, std::vector<int> exclude), хотя в какой-то момент будет проще последовать предложению Натана Оливерса и использовать std::discrete_distribution вместо этого.

7
463035818_is_not_a_number 14 Окт 2021 в 13:27

Пример 2: Допустим, я хочу сгенерировать случайное число от 0 до 100, но это число никогда не может быть любым числом от 4 до 7.

Для этого предназначен std :: piecewise_constant_distribution.

    std::vector<int> i{0,  4, 8, 101};
    std::vector<int> w{ 4,  0,  93};
    std::piecewise_constant_distribution<> d(i.begin(), i.end(), w.begin());

Live демо

6
Marek R 14 Окт 2021 в 13:37

Если вы хотите пропустить, скажем, 4, тогда очень хороший способ (который не ставит под угрозу никакие статистические свойства генератора) - рисовать в полуоткрытом интервале [0, 99), а затем добавлять 1, если число равно 4. или выше.

Вы делаете что-то подобное, чтобы пропустить числа в диапазоне.

Этот метод - удивительно хороший способ моделирования функции квантиля , связанной с желаемым распределением вероятностей.

2
Bathsheba 14 Окт 2021 в 13:17

Вы можете использовать фильтр произвольной сложности по равномерному распределению:

template<typename D, typename G, typename F>
auto sample(D &distribution, G &generator, F const &filter)
{
    while(true)
    {
        auto const value = distribution(generator);
        if(filter(value))
            return value;
    }
}

Ваш примерный случай превращается в следующий

std::random_device rd;
std::mt19937 mt(rd());
std::uniform_int_distribution<int> probability(0, 100);

auto const filter = +[](int n) {return n < 4 || n > 7;}
int const i = sample(probability, mt, filter);

Вы должны иметь в виду, что такая фильтрация имеет свою цену.

Пусть N будет количеством различных значений, возвращаемых распределением, F - количеством этих отфильтрованных значений; затем, если вам нужно выбрать значения S, вы должны выбрать и отфильтровать значения S * N / (N - F) в среднем. Это нормально, если F мало по сравнению с N, но ужасно неэффективно, когда F приближается к N. В вашем случае, N = 100, F = 4 и N / (N - F) = 1.04166...

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

2
yielduck 14 Окт 2021 в 14:10

Есть возможность сделать это вручную в разумном диапазоне чисел ..., создать таблицу поиска и исключить недопустимые числа:

 static int rand_pool[]{1,2,3,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23}; //no number 4
    srand((int)time(0));
        int random_number = rand_pool[rand() % 22];
-1
Alex Pina 14 Окт 2021 в 13:15