Мне нужна функция, которая генерирует случайное целое число в заданном диапазоне (включая значения границ). У меня нет необоснованных требований к качеству / случайности, у меня есть четыре требования:
- Мне нужно, чтобы это было быстро. Мой проект должен генерировать миллионы (а иногда даже десятки миллионов) случайных чисел, и моя текущая функция генератора оказалась узким местом.
- Мне нужно, чтобы он был достаточно равномерным (использование rand () прекрасно).
- диапазон минимальных и максимальных значений может быть от <0, 1> до <-32727, 32727>.
- это должно быть посеянным.
В настоящее время у меня есть следующий код C ++:
output = min + (rand() * (int)(max - min) / RAND_MAX)
Проблема в том, что он не является действительно единообразным - max возвращается только тогда, когда rand () = RAND_MAX (для Visual C ++ это 1/32727). Это главная проблема для небольших диапазонов, таких как <-1, 1>, где последнее значение почти никогда не возвращается.
Поэтому я взял ручку и бумагу и придумал следующую формулу (которая основывается на трюке округления целых чисел (int) (n + 0,5)):
Но это все еще не дает мне равномерное распределение. Повторные прогоны с 10000 выборками дают мне соотношение 37:50:13 для значений значений -1, 0,1.
Не могли бы вы предложить лучшую формулу? (или даже целая функция генератора псевдослучайных чисел)
Ответы:
Быстрое, несколько лучшее, чем у вас, но все еще не правильно распределенное решение
За исключением случаев, когда размер диапазона является степенью 2, этот метод создает смещенные неоднородные распределенные числа независимо от качества
rand()
. Для всесторонней проверки качества этого метода, пожалуйста, прочитайте это .источник
rand()
в C ++ следует считать вредными, есть гораздо лучшие способы получить что-то равномерно распределенное и фактически случайное.Самый простой (и, следовательно, лучший) ответ C ++ (используя стандарт 2011 года)
Не нужно заново изобретать колесо. Не нужно беспокоиться о предвзятости. Не нужно беспокоиться об использовании времени в качестве случайного семени.
источник
random_device
, которые в некоторых случаях могут быть полностью нарушены . Более того,mt19937
хотя это очень хороший выбор общего назначения, он не является самым быстрым из генераторов хорошего качества (см. Это сравнение ) и, следовательно, может быть не идеальным кандидатом на ОП.minstd
такой метод будет), но это прогресс. Что касается плохой реализацииrandom_device
- это ужасно и должно рассматриваться как ошибка (возможно, также стандарта C ++, если это позволяет).rand()
вариант не подходит, и имеет ли это значение для некритического использования, например, для генерации случайного сводного индекса? Кроме того, я должен беспокоиться о созданииrandom_device
/mt19937
/uniform_int_distribution
в узком цикле / встроенной функции? Должен ли я предпочесть передать их?Если ваш компилятор поддерживает C ++ 0x и его использование является вариантом для вас, тогда новый стандартный
<random>
заголовок, вероятно, удовлетворит ваши потребности. Он имеет высокое качество,uniform_int_distribution
которое принимает минимальные и максимальные границы (включительно по мере необходимости), и вы можете выбрать один из различных генераторов случайных чисел для подключения к этому распределению.Вот код, который генерирует миллион случайных
int
s, равномерно распределенных в [-57, 365]. Я использовал новые<chrono>
средства стандартизации для определения времени, так как вы упомянули, что производительность является серьезной проблемой для вас.Для меня (Intel Core i5 2,8 ГГц) это распечатывает:
2.10268e + 07 случайных чисел в секунду.
Вы можете заполнить генератор, передав int в его конструктор:
Если позже вы обнаружите, что
int
он не охватывает диапазон, необходимый для вашего дистрибутива, это можно исправить, изменивuniform_int_distribution
подобное так (например, наlong long
):Если позже вы обнаружите, что
minstd_rand
генератор недостаточно высокого качества, его также можно легко заменить. Например:Имея отдельный контроль над генератором случайных чисел, и случайное распределение может быть довольно освобождающим.
Я также вычислил (не показано) первые 4 «момента» этого распределения (используя
minstd_rand
) и сравнил их с теоретическими значениями в попытке количественно оценить качество распределения:(
x_
Префикс относится к «ожидаемому»)источник
d
на каждой итерации разные границы? Насколько это замедлит цикл?Давайте разделим проблему на две части:
n
в диапазоне от 0 до (макс-мин).Первая часть, очевидно, самая сложная. Давайте предположим, что возвращаемое значение rand () совершенно одинаково. Использование по модулю добавит смещение к первым
(RAND_MAX + 1) % (max-min+1)
числам. Так что, если мы могли бы волшебным образом изменить ,RAND_MAX
чтобыRAND_MAX - (RAND_MAX + 1) % (max-min+1)
, не было бы больше не будут какие - либо предубеждения.Оказывается, что мы можем использовать эту интуицию, если мы хотим допустить псевдо-недетерминизм во время выполнения нашего алгоритма. Всякий раз, когда rand () возвращает слишком большое число, мы просто запрашиваем другое случайное число, пока не получим достаточно маленькое число.
Время выполнения теперь распределено геометрически с ожидаемым значением,
1/p
гдеp
есть вероятность получить достаточно малое число с первой попытки. ТакRAND_MAX - (RAND_MAX + 1) % (max-min+1)
как всегда меньше чем(RAND_MAX + 1) / 2
, мы знаем этоp > 1/2
, поэтому ожидаемое количество итераций всегда будет меньше двух для любого диапазона. С помощью этой техники должна быть возможность генерировать десятки миллионов случайных чисел менее чем за секунду на стандартном процессоре.РЕДАКТИРОВАТЬ:
Хотя вышесказанное является технически правильным, ответ DSimon, вероятно, более полезен на практике. Вы не должны реализовывать это самостоятельно. Я видел много реализаций выборки отклонения, и часто очень трудно увидеть, правильно это или нет.
источник
Как насчет Мерсена Твистера ? Реализация boost довольно проста в использовании и хорошо протестирована во многих реальных приложениях. Я сам использовал его в нескольких научных проектах, таких как искусственный интеллект и эволюционные алгоритмы.
Вот их пример, где они делают простую функцию, чтобы бросить шестигранный кубик:
О, и вот еще несколько сводов этого генератора на тот случай, если вы не уверены, что вам следует использовать его гораздо хуже
rand()
:источник
boost::uniform_int
распределения), вы можете преобразовать минимальные максимальные диапазоны во все, что вам нравится, и это можно посеять.Это отображение 32768 целых чисел в (nMax-nMin + 1) целых чисел. Отображение будет очень хорошим, если (nMax-nMin + 1) мало (как в вашем требовании). Однако обратите внимание, что если (nMax-nMin + 1) велико, отображение не будет работать (например, вы не можете отобразить 32768 значений в 30000 значений с равной вероятностью). Если такие диапазоны необходимы - вы должны использовать 32-битный или 64-битный случайный источник вместо 15-битной rand () или игнорировать результаты rand (), которые выходят за пределы допустимого диапазона.
источник
RAND_MAX
на,(double) RAND_MAX
чтобы избежать целочисленного предупреждения о переполнении.Вот непредвзятая версия, которая генерирует числа в
[low, high]
:Если ваш диапазон достаточно мал, нет причин кэшировать правую часть сравнения в
do
цикле.источник
[0, h)
для простоты. Вызовrand()
имеетRAND_MAX + 1
возможные возвращаемые значения; принимаяrand() % h
обвалы(RAND_MAX + 1) / h
из них к каждому изh
выходных значений, за исключением того, что(RAND_MAX + 1) / h + 1
они преобразуются в значение, которые меньше(RAND_MAX + 1) % h
(из - за последний частичный цикл черезh
выходы). Поэтому мы удаляем(RAND_MAX + 1) % h
возможные выходные данные, чтобы получить беспристрастное распределение.Я рекомендую библиотеку Boost.Random , она очень детализирована и хорошо документирована, она позволяет вам явно указать, какой дистрибутив вы хотите, и в не криптографических сценариях может фактически превзойти типичную реализацию ранда библиотеки C.
источник
предположим, что min и max являются значениями int, [и] означает, что включают это значение, (и) означает, что не включают это значение, используя выше, чтобы получить правильное значение с помощью c ++ rand ()
ссылка: для () [] определить, посетить:
https://en.wikipedia.org/wiki/Interval_(mathematics)
для функций rand и srand или определения RAND_MAX посетите:
http://en.cppreference.com/w/cpp/numeric/random/rand
[мин Макс]
(мин Макс]
[мин Макс)
(мин Макс)
источник
В этой теме выборка отклонения уже обсуждалась, но я хотел предложить одну оптимизацию, основанную на том факте, что
rand() % 2^something
она не вносит никакого смещения, как уже упоминалось выше.Алгоритм действительно прост:
Вот мой пример кода:
Это хорошо работает, особенно для небольших интервалов, потому что степень 2 будет «ближе» к реальной длине интервала, и поэтому число промахов будет меньше.
PS
Очевидно, что избегать рекурсии было бы более эффективно (не нужно вычислять снова и снова потолок бревна ...), но я подумал, что это будет более читабельно для этого примера.
источник
Обратите внимание, что в большинстве предложений начальное случайное значение, которое вы получаете от функции rand (), обычно от 0 до RAND_MAX, просто теряется. Вы создаете только одно случайное число из него, в то время как есть надежная процедура, которая может дать вам больше.
Предположим, что вы хотите [min, max] область целых случайных чисел. Начнем с [0, max-min]
Взять базу b = max-min + 1
Начните с представления числа, полученного вами из rand () в базе b.
Таким образом, вы получите слово (log (b, RAND_MAX)), потому что каждая цифра в базе b, за исключением, возможно, последней, представляет случайное число в диапазоне [0, max-min].
Конечно, последний сдвиг к [min, max] прост для каждого случайного числа r + min.
Если NUM_DIGIT - это число цифр в базе b, которое вы можете извлечь, и это
тогда вышеизложенное представляет собой простую реализацию извлечения случайных чисел NUM_DIGIT от 0 до b-1 из одного случайного числа RAND_MAX, обеспечивая b <RAND_MAX.
источник
Формула для этого очень проста, поэтому попробуйте это выражение,
источник
int num = (int) rand() % (max - min) + min;
Следующее выражение должно быть беспристрастным, если я не ошибаюсь:
Здесь я предполагаю, что rand () дает вам случайное значение в диапазоне от 0,0 до 1,0, НЕ включая 1,0, и что max и min являются целыми числами с условием, что min <max.
источник
std::floor
возвращаетdouble
, и нам нужно целочисленное значение здесь. Я бы просто использовалint
вместо того, чтобы использоватьstd::floor
.