Итак, я сделал программу, которая имитирует вещи, и в ней я заметил, что функция c++ rand() слишком часто генерирует низкие числа, поэтому я попытался ее протестировать.
#include <iostream>
#include <fstream>
#include <stdio.h>
#include <vector>
#include <cstdlib>
#include <time.h>
#include <cfloat>
#include <iomanip>
using namespace std;
int main(){
srand(time(NULL));
int qwerty=0;
for(int i=0; i<10000000;i++){
if (rand()%10000<2800){
qwerty++;
}
}
cout << qwerty << endl;
return 0;
}
Если бы я запустил файл с этим «для тестера», я бы постоянно получал число около 3400000, или 34%, что близко к 34%, которые я видел в моей реальной программе, проблема в том, что вывод должен быть около 2800000 или 28%.
Затем я попытался запустить это «для тестера» в новом проекте (то же самое, что я написал здесь), где присутствовали только библиотеки и srand (время (NULL)) с тем же результатом.
Затем я попытался скопировать этот файл внутри онлайн-компилятора, на этот раз вместо 3400000 я получил правильное число 2800000.
Я не могу найти, почему это происходит, кто-нибудь знает?
Дополнительная информация: Я использую dev-c++ в качестве IDE с 64-битной версией TDM-GCC 4.9.2 и ISO C++11. Если я возьму исполняемый файл, созданный на моем компьютере, и запущу его на другом, я получу тот же результат 34%, Windows 10 — это операционная система. Эта проблема возникает также, если я использую разные номера.
rand
и по модулю действительно плохо подходят для хороших распределений. Если вы можете использовать C++11, используйте std::mt19337
для генератора случайных чисел и std::uniform_int_distribution
для получения значений в нужном вам диапазоне.
Что такое RAND_MAX
на вашей платформе?
Примечание: Вот ссылка на более свежую версию DevCpp. Вы, вероятно, захотите получить версию, которая поставляется в комплекте с набором инструментов GCC9.2, но если вы хотите легко идти в ногу с развивающимся компилятором GCC (и получить большую экосистему готовых инструментов), рассмотрите возможность использования одной из загрузок без GCC и вместо этого установите MSYS2 и используйте его для установки набора инструментов.
Я считаю, что нужно вообще избегать dev-С++.
rand
, почему вам больше не следует его использовать и как использовать <random>
.
@ user4581301: За исключением того, что здесь проблема не в генераторе - настоящий генератор показал бы аналогичный эффект. Даже опытные ученые злоупотребляют компьютерными случайными последовательностями.
@Downvoters, этот вопрос глубже, чем кажется на первый взгляд.
Но не много. rand()
и по модулю — это PB&J старой школы генерации случайных чисел в диапазоне. Не верю, что можно впихнуть rand()
ни в один из современных дистрибутивов. rand()
в вакууме может быть вполне приемлемо, но наивно вести себя так, как это используется. Мое мнение таково, что он по праву заслужил плохую репутацию, и нам следует просто перейти к более новым инструментам или сторонней библиотеке в зависимости от ваших потребностей.
@Bathsheba Я проверял, но забыл включить, это 32767
@sweenish Я использовал rand, потому что это то, чему меня учили как в старшей школе (для C), так и в университете прямо сейчас (императивный C++), я уверен, что больше не буду его использовать.
Спасибо всем за ответ
Это хорошо известная проблема с %
и редкий случай, когда rand
не виноват.
Для примера рассмотрим RAND_MAX == 2
. Далее предположим, что rand()
совершенно однородно. Затем вы получаете числа 0
, 1
и 2
. Теперь посмотрите на это:
int x = rand() % 2;
Если распределение рандов
rand() P
0 1/3 33.33333 %
1 1/3 33.33333 %
2 1/3 33.33333 %
Тогда результирующее распределение x
:
x P
0 2/3 66.66666 %
1 1/3 33.33333 %
Решение: используйте средства, представленные в <random>
.
ты имеешь в виду 33.33333
Для равномерно распределенной случайной величины E в открытом интервале [0, 32767] вероятность того, что mod(E, 10000) < 2800, составляет около 34%. Интуитивно вы можете думать о mod(E, 10000) < 2800 как о преимуществе ведра чисел в диапазоне [30000, 32767]: это ведро по модулю 10000 всегда меньше 2800. Таким образом, это приводит к увеличению результата выше 28%. .
Это поведение, которое вы наблюдаете здесь.
Это не зависит от качества генератора случайных чисел, хотя вы получите лучшие результаты, если будете использовать однородный генератор с большей периодичностью. Использование rand()
из вашей стандартной библиотеки C++ не рекомендуется, поскольку стандарт слишком смягчен в отношении требований к функциям, чтобы он был переносимым. <random>
из C++11 доставит вам гораздо меньше хлопот: вы также сможете избежать явного %
.
Но это все еще очень хорошая причина, чтобы избегать rand()
+ %
, как правило.
@Sweenish Это веская причина относиться к %
с осторожностью с любым генератором случайных чисел.
Если вы хотите обобщить, конечно. Но если кто-то использует модуль по модулю с твистером Мерсенна, это намного проще указать в обзоре. Но я присоединяюсь к философии «rand()
считается вредным». У нас есть лучшие инструменты, просто используйте их.
@sweenish: Основная проблема с rand()
, помимо небезопасности потоков, заключается в том, что стандарт C++ удивительно гибок в отношении своих требований. Это означает, что в любом математическом программном обеспечении вы в конечном итоге создаете свою собственную версию. Тем не менее, он может быть очень полезным — он требует меньше состояния, чем MT, и работает очень быстро на современных платформах. Усовершенствования, такие как перетасовка Бейса-Дарема, могут помочь ему достичь достаточной статистической достоверности, чтобы пройти даже тесты Дихарда.
Это звучит как серьезные проблемы для меня. Что только усиливает мою точку зрения, на мой взгляд. Если мы собираемся пойти своим путем, это тоже хорошо. Я знаю о недостатках PRNG Стандартной библиотеки. Но это звучит так, будто rand()
никогда не следует использовать. Если мне нужно достаточно позаботиться, чтобы узнать о реализации, мне лучше воспользоваться сторонней библиотекой.
Вот как ведет себя
rand()
. Интервалы, в которых он работает, и интервал, который вы хотите, не делятся, поэтому последнее ведро, которое может использоватьrand()
, не заполнено. Другими словами, у него больше младших значений, из чего следует, что они появляются чаще. Обновите все свои инструменты и используйте что-то вродеstd::mt19937
из<random>
.