Производить случайные числа равномерно по всему диапазону

Мне нужно генерировать случайные числа в течение заданного интервала, [max; min].

Кроме того, случайные числа должны быть равномерно распределены по интервалу, не расположенному к определенной точке.

Currenly Я генерирую как:

for(int i=0; i<6; i++)
{
    DWORD random = rand()%(max-min+1) + min;
}

Из моих тестов случайные числа генерируются только в одной точке.

Example
min = 3604607;
max = 7654607;

Произвольные числа:

3631594
3609293
3630000
3628441
3636376
3621404

Из ответов ниже: OK, RAND_MAX - 32767. Я на платформе Windows С++. Есть ли другой способ генерации случайных чисел с равномерным распределением?

Ответ 1

Почему rand это плохая идея

Большинство ответов, которые вы получили здесь, используют функцию rand и оператор модуля. Этот метод не может генерировать числа равномерно (это зависит от диапазона и значения RAND_MAX) и поэтому не рекомендуется.

С++ 11 и поколение в диапазоне

С С++ 11 возросло множество других опций. Одно из тех, что соответствует вашим требованиям для генерации случайного числа в диапазоне, довольно приятно: std::uniform_int_distribution. Вот пример:

const int range_from  = 0;
const int range_to    = 10;
std::random_device                  rand_dev;
std::mt19937                        generator(rand_dev());
std::uniform_int_distribution<int>  distr(range_from, range_to);

std::cout << distr(generator) << '\n';

И вот бегущий пример.

Другие случайные генераторы

Заголовок <random> предлагает бесчисленное множество других генераторов случайных чисел с различными типами распределений, включая Бернулли, Пуассона и нормаль.

Как я могу перетасовать контейнер?

Стандарт предоставляет std::shuffle, который можно использовать следующим образом:

std::vector<int> vec = {4, 8, 15, 16, 23, 42};

std::random_device random_dev;
std::mt19937       generator(random_dev());

std::shuffle(vec.begin(), vec.end(), generator);

Алгоритм будет переупорядочивать элементы случайным образом с линейной сложностью.

Boost.Random

Другой вариант, если у вас нет доступа к компилятору С++ 11 +, это использовать Boost.Random. Его интерфейс очень похож на интерфейс С++ 11.

Ответ 2

[edit] Предупреждение: не используйте rand() для статистики, симуляции, криптографии или чего-либо серьезного.

Достаточно хорошо, чтобы цифры выглядели случайными для типичного человека в спешке, не более.

Посмотрите ответ @Jefffrey для лучших вариантов или этот ответ для крипто-безопасных случайных чисел.


Как правило, старшие биты показывают лучшее распределение, чем младшие биты, поэтому рекомендуемый способ генерации случайных чисел диапазона для простых целей:

((double) rand() / (RAND_MAX+1)) * (max-min+1) + min

Примечание: убедитесь, что RAND_MAX + 1 не переполняется (спасибо Деми)!

Деление генерирует случайное число в интервале [0, 1); "растянуть" это до необходимого диапазона. Только когда max-min + 1 приближается к RAND_MAX, вам нужна функция "BigRand()", как это было опубликовано Марком Рэнсомом.

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


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

Ответ 3

Я хотел бы дополнить Angry Shoe и peterchen отличными ответами с кратким обзором уровня техники в 2015 году:

Некоторые хорошие варианты

randutils

Библиотека randutils (презентация) - интересная новинка, предлагающая простой интерфейс и (объявленные) надежные случайные возможности. У этого есть недостатки, что он добавляет зависимость от вашего проекта и, будучи новым, он не был широко протестирован. В любом случае, будучи свободным (лицензия MIT) и только заголовок, я думаю, что стоит попробовать.

Минимальный образец: бросок кубика

#include <iostream>
#include "randutils.hpp"
int main() {
    randutils::mt19937_rng rng;
    std::cout << rng.uniform(1,6) << "\n";
}

Даже если кто-то не интересуется библиотекой, веб-сайт (http://www.pcg-random.org/) содержит много интересных статей о теме генерации случайных чисел в общем и в библиотеке С++ в частности.

Boost.Random

Boost.Random (документация) - это библиотека, которая вдохновила С++ 11 <random>, с которой разделяет большую часть интерфейса. Хотя теоретически также являясь внешней зависимостью, Boost уже стал статусом "квазистандартной" библиотеки, а его случайный модуль можно рассматривать как классический выбор для генерации случайных чисел хорошего качества. Он имеет два преимущества в отношении решения С++ 11:

  • он более портативен, просто требуется поддержка компилятора для С++ 03
  • его random_device использует системные методы, предлагающие выборку хорошего качества.

Единственным недостатком является то, что модуль, предлагающий random_device, не является только заголовком, нужно скомпилировать и связать boost_random.

Минимальный образец: бросок кубика

#include <iostream>
#include <boost/random.hpp>
#include <boost/nondet_random.hpp>

int main() {
    boost::random::random_device                  rand_dev;
    boost::random::mt19937                        generator(rand_dev());
    boost::random::uniform_int_distribution<>     distr(1, 6);

    std::cout << distr(generator) << '\n';
}

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

  • make mt19937 a thread_local: генератор довольно пухлый ( > 2 КБ) и лучше не выделяется в стеке
  • seed mt19937 с более чем одним целым числом: Mersenne Twister имеет большое состояние и может использовать больше энтропии во время инициализации

Некоторые не очень хорошие варианты

Библиотека С++ 11

Будучи самым идиоматическим решением, библиотека <random> не предлагает многого в обмен на сложность интерфейса даже для удовлетворения основных потребностей. Недостаток находится в std::random_device: стандарт не дает минимального качества для своего вывода (пока entropy() возвращает 0), а с 2015 года MinGW (не самый используемый компилятор, но вряд ли эзотерический выбор ) всегда будет печатать 4 на минимальном образце.

Минимальный образец: бросок кубика

#include <iostream>
#include <random>
int main() {
    std::random_device                  rand_dev;
    std::mt19937                        generator(rand_dev());
    std::uniform_int_distribution<int>  distr(1, 6);

    std::cout << distr(generator) << '\n';
}

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

Годовое решение

Минимальный образец: бросок кубика

#include <iostream>
#include <random>

int main() {
    std::cout << std::randint(1,6);
}

Это простое, эффективное и аккуратное решение. Только дефект, потребуется некоторое время, чтобы скомпилировать - около двух лет, при условии, что С++ 17 будет выпущен вовремя, и экспериментальная функция randint будет утверждена в новый Стандарт. Возможно, к тому времени гарантии качества посева улучшатся.

хуже - лучше решение

Минимальный образец: бросок кубика

#include <cstdlib>
#include <ctime>
#include <iostream>

    int main() {
        std::srand(std::time(nullptr));
        std::cout << (std::rand() % 6 + 1);
    }

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

Учетный тролль-решение

Минимальный образец: бросок кубика

#include <iostream>

int main() {
    std::cout << 9;   // http://dilbert.com/strip/2001-10-25
}

В то время как 9 - несколько необычный результат для регулярного броска броска, нужно восхищаться превосходным сочетанием хороших качеств в этом решении, которое управляется как самый быстрый, простой, самый удобный для кеша и самый портативный. Подставляя 9 в 4, человек получает идеальный генератор для любого типа Подземелий и Драконов, умирающих, но все же избегая нагрузок, обозначенных символами 1, 2 и 3. Единственный небольшой недостаток состоит в том, что из-за плохого характера троллей, эта программа действительно порождает поведение undefined.

Ответ 4

Если RAND_MAX 32767, вы можете удвоить количество бит.

int BigRand()
{
    assert(INT_MAX/(RAND_MAX+1) > RAND_MAX);
    return rand() * (RAND_MAX+1) + rand();
}

Ответ 6

Если вас беспокоит случайность, а не скорость, вы должны использовать метод генерации случайных чисел. Существует несколько способов сделать это... Самый простой способ - использовать OpenSSL Генератор случайных чисел.

Вы также можете написать свой собственный, используя алгоритм шифрования (например, AES). Выбрав семя и IV, а затем непрерывно перешифровывая вывод функции шифрования. Использование OpenSSL проще, но менее мужественное.

Ответ 7

Вы должны посмотреть RAND_MAX для своего конкретного компилятора/среды. Я думаю, вы увидите эти результаты, если rand() создает случайное 16-битное число. (вы, кажется, предполагаете, что это будет 32-разрядное число).

Я не могу обещать, что это ответ, но, пожалуйста, опубликуйте ваше значение RAND_MAX и немного подробнее о вашей среде.

Ответ 9

Если вы хотите, чтобы числа были равномерно распределены по диапазону, вы должны разбить свой диапазон на несколько равных секций, которые представляют количество необходимых вам очков. Затем получите случайное число с min/max для каждого раздела.

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

Ответ 10

Это не код, но эта логика может вам помочь.

static double rnd(void)
{
return (1.0/(RAND_MAX+1.0)*((double)(rand())) );
}

static void InitBetterRnd(unsigned int seed)
{
register int i;
srand( seed );
for( i=0; i<POOLSIZE; i++){
pool[i]= rnd();
}
}

 static double rnd0_1(void)
 {  // This function returns a number between 0 and 1
static int i=POOLSIZE-1;
double r;

i = (int)(POOLSIZE*pool[i]);
r=pool[i];
pool[i]=rnd();
return (r);
}

Ответ 11

Это должно обеспечить равномерное распределение по диапазону [low, high) без использования float, если общий диапазон меньше RAND_MAX.

uint32_t rand_range_low(uint32_t low, uint32_t high)
{
    uint32_t val;
    // only for 0 < range <= RAND_MAX
    assert(low < high);
    assert(high - low <= RAND_MAX);

    uint32_t range = high-low;
    uint32_t scale = RAND_MAX/range;
    do {
        val = rand();
    } while (val >= scale * range); // since scale is truncated, pick a new val until it lower than scale*range
    return val/scale + low;
}

и для значений, больших RAND_MAX, вы хотите что-то вроде

uint32_t rand_range(uint32_t low, uint32_t high)
{
    assert(high>low);
    uint32_t val;
    uint32_t range = high-low;
    if (range < RAND_MAX)
        return rand_range_low(low, high);
    uint32_t scale = range/RAND_MAX;
    do {
        val = rand() + rand_range(0, scale) * RAND_MAX; // scale the initial range in RAND_MAX steps, then add an offset to get a uniform interval
    } while (val >= range);
    return val + low;
}

Это примерно то, как std:: uniform_int_distribution делает вещи.

Ответ 12

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

Но имейте в виду, что случайность не обязательно однородна.

Изменить: я добавил "маленький образец", чтобы уточнить.

Ответ 13

Решение, заданное man 3 rand для числа от 1 до 10 включительно:

j = 1 + (int) (10.0 * (rand() / (RAND_MAX + 1.0)));

В вашем случае это будет:

j = min + (int) ((max-min+1) * (rand() / (RAND_MAX + 1.0)));

Конечно, это не идеальная случайность или единообразие, как указывают некоторые другие сообщения, но этого достаточно для большинства случаев.

Ответ 14

@Solution ((double) rand() / (RAND_MAX+1)) * (max-min+1) + min

Предупреждение. Не забывайте из-за растягивания и возможных ошибок точности (даже если RAND_MAX был достаточно большим), вы сможете создавать равномерно распределенные "бункеры", а не все числа в [мин, макс].


@Solution: Bigrand

Предупреждение. Обратите внимание: это удваивает бит, но все равно не сможет генерировать все числа в вашем диапазоне в целом, т.е. не обязательно верно, что BigRand() будет генерировать все числа в диапазоне.


Информация. Ваш подход (по модулю) является "точным", если диапазон rand() превышает диапазон интервалов, а rand() является "равномерным". Ошибка не более первых максимальных номеров - 1/(RAND_MAX +1).

Кроме того, я предлагаю переключиться на новый случайный пакет e на С++ 11, который предлагает лучшие и более разнообразные реализации, чем Rand().

Ответ 15

Конечно, следующий код не даст вам случайных чисел, а будет псевдослучайным числом. Используйте следующий код

#define QUICK_RAND(m,n) m + ( std::rand() % ( (n) - (m) + 1 ) )

Например:

int myRand = QUICK_RAND(10, 20);

Вы должны позвонить

srand(time(0));  // Initialize random number generator.

иначе числа не будут случайными.

Ответ 16

Я только что нашел это в Интернете. Это должно работать:

DWORD random = ((min) + rand()/(RAND_MAX + 1.0) * ((max) - (min) + 1));