Первое случайное число всегда меньше, чем остальные
Мне кажется, что в С++ первое случайное число, вызываемое с помощью метода std rand(), в большинстве случаев значительно меньше второго. Что касается реализации Qt, первая из них почти всегда на несколько величин меньше.
qsrand(QTime::currentTime().msec());
qDebug() << "qt1: " << qrand();
qDebug() << "qt2: " << qrand();
srand((unsigned int) time(0));
std::cout << "std1: " << rand() << std::endl;
std::cout << "std2: " << rand() << std::endl;
выход:
qt1: 7109361
qt2: 1375429742
std1: 871649082
std2: 1820164987
Предполагается ли это, из-за ошибки в посеве или ошибке?
Кроме того, в то время как выход qrand() сильно меняется, первый вывод rand(), по-видимому, изменяется со временем линейно. Просто интересно, почему.
Ответы
Ответ 1
Я не уверен, что это может быть классифицировано как ошибка, но есть объяснение. Давайте рассмотрим ситуацию:
-
Посмотрите rand-реализацию. Вы увидите это только вычисление с использованием последнего сгенерированного значения.
-
Вы посеяли с помощью QTime:: currentTime(). msec(), которая по своей природе ограничена небольшим диапазоном значений 0..999, но qsrand принимает переменную uint в диапазоне 0. 0,4294967295.
Объединив эти два фактора, у вас есть шаблон.
Просто из любопытства: попробуйте посеять с помощью QTime:: currentTime(). msec() + 100000000
Теперь первое значение, вероятно, будет больше второго раза.
Я бы не стал слишком беспокоиться. Этот "шаблон", похоже, происходит только при первых двух сгенерированных значениях. После этого все, кажется, возвращается к норме.
EDIT:
Чтобы сделать все более понятным, попробуйте выполнить приведенный ниже код. Он будет сравнивать первые два сгенерированных значения, чтобы увидеть, какой из них меньше, используя все возможные миллисекундные значения (диапазон: 0..999) в качестве семени:
int totalCalls, leftIsSmaller = 0;
for (totalCalls = 0; totalCalls < 1000; totalCalls++)
{
qsrand(totalCalls);
if (qrand() < qrand())
leftIsSmaller++;
}
qDebug() << (100.0 * leftIsSmaller) / totalCalls;
Он напечатает 94.8, что означает 94.8% времени, когда первое значение будет меньше второго.
Вывод: при использовании текущей миллисекунды для семени, вы увидите этот шаблон для первых двух значений. Я сделал несколько тестов здесь, и шаблон, кажется, исчезает после создания второго значения. Мой совет: найдите "хорошее" значение для вызова qsrand (который, очевидно, должен быть вызван только один раз, в начале вашей программы). Хорошее значение должно охватывать весь диапазон класса uint. Взгляните на этот другой вопрос для некоторых идей:
Кроме того, взгляните на это:
Ответ 2
В текущем стандарте Qt или C нет стандартного рандомизатора качества, и ваш тест показывает. Qt, похоже, использует для этого время выполнения C (это легко проверить, но почему). Если С++ 11 доступен в вашем проекте, используйте гораздо лучший и способ более надежный метод:
#include <random>
#include <chrono>
auto seed = std::chrono::system_clock::now().time_since_epoch().count();
std::default_random_engine generator(seed);
std::uniform_int_distribution<uint> distribution;
uint randomUint = distribution(generator);
Существует хорошее видео, которое охватывает тему. Как заметил комментатор user2357112, мы можем применять разные случайные механизмы, а затем разные дистрибутивы, но для моего конкретного использования вышеописанное работает очень хорошо.
Ответ 3
Имея в виду, что принятие суждений о статистических явлениях, основанных на небольшом числе образцов, может ввести в заблуждение, я решил провести небольшой эксперимент. Я запускаю следующий код:
int main()
{
int i = 0;
int j = 0;
while (i < RAND_MAX)
{
srand(time(NULL));
int r1 = rand();
int r2 = rand();
if (r1 < r2)
++j;
++i;
if (i%10000 == 0) {
printf("%g\n", (float)j / (float)i);
}
}
}
который в основном печатал процентное количество раз, когда первое сгенерированное число было меньше второго. Ниже вы видите график этого отношения:
![enter image description here]()
и, как вы видите, он действительно приближается к 0,5 после менее 50 фактических новых семян.
Как было предложено в комментарии, мы могли бы модифицировать код для использования последовательных семян на каждой итерации и ускорить конвергенцию:
int main()
{
int i = 0;
int j = 0;
int t = time(NULL);
while (i < RAND_MAX)
{
srand(t);
int r1 = rand();
int r2 = rand();
if (r1 < r2)
++j;
++i;
if (i%10000 == 0) {
printf("%g\n", (float)j / (float)i);
}
++t;
}
}
Это дает нам:
![enter image description here]()
который также близок к 0.5.
Хотя rand
, безусловно, не лучший генератор псевдослучайных чисел, утверждение о том, что он часто генерирует меньшее число во время первого запуска, кажется не оправданным.