2

This is a seemingly common question, so I hope I don't sound redundant. But the range returned from rand() should be between 0 and RAND_MAX, however, when I do a very simple rand statement, I'm always getting returns within a very small range.

This range is something like 1,4XX,XXX,XXX. I figured this might be a clock thing, so I waited thirty minutes and I'm still getting numbers in the same range.

Here is some sample output from twenty minutes ago:

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

and here is sample output from just now:

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

I know rand() isn't perfect, but this seems too similar to be correct. If the range is 0 - RAND_MAX, it seems weird that every number returned is in the same range.

Here is the code I was testing:

#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;
}

I don't think I needed all those #include statements, but I saw other people using them so I included them just in case it would affect my output, but it didn't.

Edit

So the links provided by @Mgetz and @Curious were super helpful. To consolidate,

Info page: http://en.cppreference.com/w/cpp/numeric/random/uniform_int_distribution

Super helpful lecture (for real, watch this): https://channel9.msdn.com/Events/GoingNative/2013/rand-Considered-Harmful

I summarized what I heard in the lecture on my own notes, just so I won't have to re-research this another time if I forget. I didn't write the code here, most of the code is within on that "Info page" linked above. Most of the comments contain information from the lecture, though it isn't verbatim from the lecture. Again, I really recommend watching that. It is dense with good information.

#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;
    }
}
radiomime
  • 118
  • 1
  • 1
  • 11
  • 5
    Have you considered using C++11 features for random number generation? – Curious Jul 12 '17 at 19:44
  • 5
    `time(NULL)` has a 1 second resolution so it will provide the same seed for every instance of your program that runs in the same second. – François Andrieux Jul 12 '17 at 19:45
  • It looks like your implementation of `srand`/`rand` is pretty bad. Similar question: https://stackoverflow.com/questions/3032726/rand-generating-the-same-number-even-with-srandtimenull-in-my-main?rq=1 – Rakete1111 Jul 12 '17 at 19:46
  • 1
    The obligatory [`rand()` considered harmful](https://channel9.msdn.com/Events/GoingNative/2013/rand-Considered-Harmful) link – Mgetz Jul 12 '17 at 20:38
  • What compiler did you use and what operating system did you run it on? – Richard Jul 12 '17 at 20:50
  • I'm compiling g++ on Mac OSX 10.12.4 – radiomime Jul 12 '17 at 21:05
  • @wrightMatthew: Do you know the g++ version? – Richard Jul 12 '17 at 21:09
  • @wrightMatthew: I'm also _very_ curious if you could provide values from `std::cout< – Richard Jul 12 '17 at 21:29
  • @Richard So outputs from time(NULL) are, 1499896059, 1499896060, 1499896061, 1499896062 -- Waiting two seconds between calls --, 1499896064, 1499896066. It makes sense, it is just getting a time, incrementing by seconds. my assumption is rand() really gives a small amount of entropy, (32XXX according to a video), and then adds it to the time and maybe shifts it. So rand() just seems to be bad. The link Mgetz provided was very helpful to understand what was happening. Also, my g++ version, though it might not be relevant anymore, is Apple LLVM version 8.0.0 (clang-800.0.38) – radiomime Jul 12 '17 at 21:49
  • @wrightMatthew: I'm sorry, but do you also have the random numbers associated with the times? I should have asked for: `auto ts=time(NULL);srand(ts);std::cout< – Richard Jul 12 '17 at 21:53
  • (I've edited the question to contain the outputs in text format, so that the numbers can easily be copied by others.) – Richard Jul 12 '17 at 21:59
  • @wrightMatthew: When you say "it is just getting a time, incrementing by seconds", are you aware that the value returned by `time(NULL)` does not need to be a number of seconds (though it generally is)? One hypothesis I had about the behaviour you were seeing is that you were getting a return from `time(NULL)` with few low-order bits set. Casting this to an `int` would then throw away most of your seed. – Richard Jul 12 '17 at 22:17
  • Also, Apple LLVM clang and g++ are two different things. You can get the version with `clang++ --version` or `g++ --version`. – Richard Jul 12 '17 at 22:18

1 Answers1

6

Using the modulus operator brings in some amount of bias in the resulting "random number". Further the working of the rand() function is implementation defined and does not follow a standard algorithm across platforms.

Consider using more modern C++11 random number generation features that use standard widely accepted random number generation algorithms, that work the same across platforms (given the same seed of course).

See the following example from the cppreference page for 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';
}

Here is link to a great talk by Stephan Levavej that goes into this more in depth https://channel9.msdn.com/Events/GoingNative/2013/rand-Considered-Harmful

Curious
  • 20,870
  • 8
  • 61
  • 146
  • 1
    my comment on the OP's question links to a video that explains this in a lot more depth. – Mgetz Jul 12 '17 at 20:40
  • Great video link. Thank you, that was very helpful to help understand the issue. – radiomime Jul 12 '17 at 21:42
  • 1
    OP's using a black box and having problems. This answer suggests that OP use a different black box, implicitly promising that it won't have problems. Or that said problems are not worth caring about. I disagree with this promise. `std::random_device` may be deterministic; it may block for unknown periods ([link](http://www.pcg-random.org/posts/cpps-random_device.html)). Further, seeding the MT this way leaves a lot of the state deterministic, which can result in odd effects ([link](http://www.pcg-random.org/posts/cpp-seeding-surprises.html)). – Richard Jul 12 '17 at 22:43
  • Stephan Levavej's talk is, in a meta-sense, a criticism of propagating and using PRNGs without thinking critically about what they do. Let's not repeat the mistakes of the past and, instead, try to ensure that we talk about randomness with the reverence and care it deserves. – Richard Jul 12 '17 at 22:47
  • @Richard Sure, but known unknowns are better than unknown unknowns. Especially when they stem from the C++ standard library. – Curious Jul 12 '17 at 23:29