Подтвердить что ты не робот

Первое случайное число всегда меньше, чем остальные

Мне кажется, что в С++ первое случайное число, вызываемое с помощью метода 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(), по-видимому, изменяется со временем линейно. Просто интересно, почему.

4b9b3361

Ответ 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, безусловно, не лучший генератор псевдослучайных чисел, утверждение о том, что он часто генерирует меньшее число во время первого запуска, кажется не оправданным.