rand() с srand() дает странно похожие результаты. Возврат от rand() очень похож

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

Этот диапазон примерно равен 1,4XX,XXX,XXX. Я подумал, что это могут быть часы, поэтому я подождал тридцать минут, и я все еще получаю числа в том же диапазоне.

Вот пример вывода двадцатиминутной давности:

Matthews-Macbook-Pro:Data_Structures matthewwright$ ./main
1439810968
80
Matthews-Macbook-Pro:Data_Structures matthewwright$ ./main
1439827775
29
Matthews-Macbook-Pro:Data_Structures matthewwright$ ./main
1439827775
29
Matthews-Macbook-Pro:Data_Structures matthewwright$ ./main
1439844582
78
Matthews-Macbook-Pro:Data_Structures matthewwright$ ./main
1439878196
29
Matthews-Macbook-Pro:Data_Structures matthewwright$ ./main
1439895003
78

и вот пример вывода только что:

Matthews-Macbook-Pro:Data_Structures matthewwright$ ./main
1456483512
78
Matthews-Macbook-Pro:Data_Structures matthewwright$ ./main
1456500319
80
Matthews-Macbook-Pro:Data_Structures matthewwright$ ./main
1456500319
80
Matthews-Macbook-Pro:Data_Structures matthewwright$ ./main
1456517126
29
Matthews-Macbook-Pro:Data_Structures matthewwright$ ./main
1456533933
78

Я знаю, что rand() не идеален, но это кажется слишком похожим, чтобы быть правильным. Если диапазон равен 0 - RAND_MAX, кажется странным, что все возвращаемые числа находятся в одном и том же диапазоне.

Вот код, который я тестировал:

#include <iostream>
#include <stdio.h>      /* printf, scanf, puts, NULL */
#include <stdlib.h>     /* srand, rand */
#include <time.h>       /* time */

int main(int argc, char const *argv[])
{
    /* declarations */
    srand(time(NULL));

    std::cout << std::rand() << std::endl;
    std::cout << std::rand()%100 << std::endl;
    return 0;
}

Я не думаю, что мне нужны все эти операторы #include, но я видел, как их используют другие люди, поэтому я включил их на тот случай, если это повлияет на мой вывод, но это не так.

Редактировать

Поэтому ссылки, предоставленные @Mgetz и @Curious, оказались очень полезными. Чтобы консолидировать,

Информационная страница: http://en.cppreference.com/w/cpp/numeric/random/uniform_int_distribution

Очень полезная лекция (на самом деле, посмотрите это): https://channel9.msdn.com/Events/GoingNative/2013/rand-Considered-Harmful

Я резюмировал то, что услышал на лекции, в своих заметках, просто чтобы мне не пришлось заново исследовать это в другой раз, если я забуду. Я не писал код здесь, большая часть кода находится на этой «Информационной странице», указанной выше. Большинство комментариев содержат информацию из лекции, хотя это не дословно из лекции. Еще раз, очень рекомендую посмотреть. Он насыщен хорошей информацией.

#include <iostream>
#include <random>

int main(int argc, char const *argv[])
{
    /* https://channel9.msdn.com/Events/GoingNative/2013/rand-Considered-Harmful */

    /* Randomness Verson 1 : Deterministic */
    std::mt19937 mt(1234);
    std::uniform_int_distribution<int> dist(0,127);
    /* Default is int, but we could specify others.
     * The range is [inclusive, inclusive]
     * 
     * Above is Mersenne Twister RNG. It is deterministic, meaning we can get the same result
     * if we use "std::mt19937 mt(1234)"; or something like that. This could be useful for some
     * people (He mentions games, some experiments, et cetera). It is stupid fast.
     * 
     * However, it isn't cryptographically secure, but it pretty random as random goes. If you
     * track the output though, you could guess the next numbers, so don't use it for anything
     * secure.
     */

    /* Randomness Verson 2 */
    std::random_device rd;  //Will be used to obtain a seed for the random number engine
    std::mt19937 gen(rd()); //Standard mersenne_twister_engine seeded with rd()
    std::uniform_int_distribution<int> dis(0, 127); // Inclusive
    /* This is not reproducible. This is not deterministic.
     * "Possibly Crypto-secure." Seems like using Random Device makes this near perfect random,
     * assuming some conditions. I'm not a man who's written security software, and if you are 
     * writing security software, I assume you're not looking at StackOverflow to figure out how
     * to do random numbers. The way he talked about it in the lecture made this seem much more 
     * secure, but I'm not sure what I'm talking about when it comes to these things
     */

    for (int i = 0; i < 3; ++i)
    {
        /* Below would output the pure Mersenne Twister output, deterministic. This seems to
         * be pretty random, but it isn't totally random. */
        std::cout << dist(mt) << " ";

        /* And below would output the random device output. This should be slower, but
         * more truly random. */

        //Use dis to transform the random unsigned int generated by gen into an int in [1, 6]
        std::cout << dis(gen) << " ";

        std::cout<< std::endl;
    }
}

person wrightMatthew    schedule 12.07.2017    source источник
comment
Рассматривали ли вы возможность использования функций С++ 11 для генерации случайных чисел?   -  person Curious    schedule 12.07.2017
comment
time(NULL) имеет разрешение в 1 секунду, поэтому он будет предоставлять одно и то же начальное значение для каждого экземпляра вашей программы, который запускается в одну и ту же секунду.   -  person François Andrieux    schedule 12.07.2017
comment
Похоже, ваша реализация srand/rand довольно плоха. Аналогичный вопрос: stackoverflow.com/questions/3032726/   -  person Rakete1111    schedule 12.07.2017
comment
Обязательная ссылка rand() считается вредной   -  person Mgetz    schedule 12.07.2017
comment
Какой компилятор вы использовали и на какой операционной системе вы его запускали?   -  person Richard    schedule 12.07.2017
comment
Я компилирую g++ на Mac OSX 10.12.4.   -  person wrightMatthew    schedule 13.07.2017
comment
@wrightMatthew: Вы знаете версию g++?   -  person Richard    schedule 13.07.2017
comment
@wrightMatthew: мне также очень любопытно, не могли бы вы предоставить значения из std::cout<<time(NULL)<<std::endl;   -  person Richard    schedule 13.07.2017
comment
@Richard Итак, результаты time(NULL): 1499896059, 1499896060, 1499896061, 1499896062 -- Ожидание двух секунд между вызовами --, 1499896064, 1499896066. Это имеет смысл, это просто получение времени, увеличивающееся на секунды. мое предположение заключается в том, что rand() действительно дает небольшое количество энтропии (32XXX согласно видео), а затем добавляет его ко времени и, возможно, сдвигает его. Так что rand() кажется плохим. Предоставленная Mgetz ссылка очень помогла понять, что происходит. Кроме того, моя версия g++, хотя она может быть уже не актуальна, — это Apple LLVM версии 8.0.0 (clang-800.0.38).   -  person wrightMatthew    schedule 13.07.2017
comment
@wrightMatthew: извините, но у вас также есть случайные числа, связанные со временем? Я должен был попросить: auto ts=time(NULL);srand(ts);std::cout<<ts<<" "<<rand()<<std::endl;   -  person Richard    schedule 13.07.2017
comment
(Я отредактировал вопрос, чтобы он содержал результаты в текстовом формате, чтобы числа могли быть легко скопированы другими.)   -  person Richard    schedule 13.07.2017
comment
@wrightMatthew: Когда вы говорите, что это просто получение времени, увеличивающееся на секунды, знаете ли вы, что значение, возвращаемое time(NULL), не обязательно должно быть числом секунд (хотя обычно это так)? Одна из гипотез, которые у меня были относительно поведения, которое вы видели, заключалась в том, что вы получали возврат от time(NULL) с несколькими установленными младшими битами. Приведение этого к int приведет к потере большей части вашего семени.   -  person Richard    schedule 13.07.2017
comment
Кроме того, Apple LLVM clang и g++ — это две разные вещи. Вы можете получить версию с clang++ --version или g++ --version.   -  person Richard    schedule 13.07.2017


Ответы (1)


Использование оператора модуля вносит некоторое смещение в результирующее «случайное число». Кроме того, работа функции rand() определяется реализацией и не следует стандартному алгоритму на разных платформах.

Рассмотрите возможность использования более современных функций генерации случайных чисел C++11, которые используют стандартные широко распространенные алгоритмы генерации случайных чисел, которые работают одинаково на разных платформах (конечно, с одним и тем же начальным числом).

См. следующий пример со страницы cppreference для std::uniform_int_distribution.

#include <random>
#include <iostream>

int main()
{
    std::random_device rd;  //Will be used to obtain a seed for the random number engine
    std::mt19937 gen(rd()); //Standard mersenne_twister_engine seeded with rd()
    std::uniform_int_distribution<> dis(1, 6);

    for (int n=0; n<10; ++n)
        //Use dis to transform the random unsigned int generated by gen into an int in [1, 6]
        std::cout << dis(gen) << ' ';
    std::cout << '\n';
}

Вот ссылка на отличный доклад Стефана Левавея, в котором этот вопрос рассматривается более подробно https://channel9.msdn.com/Events/GoingNative/2013/rand-Considered-Harmful

person Curious    schedule 12.07.2017
comment
мой комментарий к вопросу ОП ссылается на видео, которое объясняет это более подробно. - person Mgetz; 12.07.2017
comment
Отличная ссылка на видео. Спасибо, очень помогло разобраться в проблеме. - person wrightMatthew; 13.07.2017
comment
ОП использует черный ящик и у него проблемы. Этот ответ предполагает, что OP использует другой черный ящик, неявно обещая, что у него не будет проблем. Или сказал, что проблемы не стоят внимания. Я не согласен с этим обещанием. std::random_device может быть детерминированным; он может быть заблокирован на неизвестный период времени (ссылка). Кроме того, заполнение MT таким образом оставляет большую часть состояния детерминированным, что может привести к странным эффектам (ссылка). - person Richard; 13.07.2017
comment
Выступление Стефана Левавея в мета-смысле является критикой распространения и использования PRNG без критического осмысления того, что они делают. Давайте не будем повторять ошибок прошлого и вместо этого постараемся, чтобы мы говорили о случайности с почтением и заботой, которых она заслуживает. - person Richard; 13.07.2017
comment
@ Ричард Конечно, но известные неизвестные лучше, чем неизвестные неизвестные. Особенно, когда они происходят из стандартной библиотеки C++. - person Curious; 13.07.2017