2

First, I know the basic principle of planting a time seed, and my program's outputs are partially random. But this baffles me.

On subsequent executions of the program, the seven randomly generated values may look like this:

14 14 47 70 84 2 24

14 28 42 52 31 10 12

63 25 4 50 20 27 56

63 19 55 44 65 60 52

14 16 17 40 54 77 4

63 6 79 36 51 85 39

The rest of the values appear random, but the first value is always either 14 or 63. Why is this happening, and how can I make it completely random?

The code is supposed to draw a random Scrabble letter without replacement, with a cout statement added for debugging purposes.

#include <iostream>
using namespace std;

int main()
{
    string bag = "AAAAAAAAABBCCDDDDEEEEEEEEEEEEFFGGGHHIIIIIIIIIJKLLLLMMNNNNNNOOOOOOOOPPQRRRRRRSSSSTTTTTTUUUUVVWWXYYZ";

    srand(time(0));
    for (int a = 0; a < 7; a++)
    {
        int i = rand()%bag.size();
        cout << i << ' ';
        bag.erase(i,1);
    }
    cout << endl;
    return 0;
}
  • Compiled in MacOS Catalina 10.15 terminal
  • Configured with: --prefix=/Library/Developer/CommandLineTools/usr --with-gxx-include-dir=/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/4.2.1
  • Apple clang version 11.0.0 (clang-1100.0.33.17)
  • Target: x86_64-apple-darwin19.6.0
  • Thread model: posix
Asteroids With Wings
  • 17,071
  • 2
  • 21
  • 35
  • 1
    Please make a [mre]. It seems you could move the code in `Player::letter` to `main` and still reproduce the behavior. – cigien Dec 07 '20 at 06:09
  • @cigien Done. ‎ – the-baby-is-you Dec 07 '20 at 06:16
  • May I offer an alternative implementation that doesn't require repeated `erase` calls? You could [std::random_shuffle](https://en.cppreference.com/w/cpp/algorithm/random_shuffle) the bag and then just look at the `a`th element of the shuffled bag. – Nathan Pierson Dec 07 '20 at 06:18
  • Thanks for the edit, it's much better now. Note that the context is still not very helpful, because it's not reproducible. Also, have you tested the code in your latest edit to see if it produces the same results, i.e. the first number is not very random? – cigien Dec 07 '20 at 06:19
  • 3
    I can't reproduce the behavior. I'm getting random looking numbers. Can you add details about which compiler you're using, as well as the compilation commands? – cigien Dec 07 '20 at 06:21
  • Yes, I'm still seeing the behavior in the second example. I honestly don't know a thing about compilers, but it's the Mac terminal running on Catalina. Command is just g++ test.cpp -o test.exe -Wall and ./test.exe. – the-baby-is-you Dec 07 '20 at 06:29
  • @cigien could still be UB then ;) but it probably isn't... – JHBonarius Dec 07 '20 at 06:52
  • Add that information to the question, along with the result of `g++ --version`. – cigien Dec 07 '20 at 06:54
  • 3
    how quickly are you successively executing the program? If you do it very fast, you might have the first number be the same, as srand(time(0)) is not seeding a different value yet. if you wait 1 or 2 seconds after each execution I have nice looking random numbers each time – geebert Dec 07 '20 at 06:55
  • @geebert I'm not doing it particularly fast, waiting longer has no effect, and separate attempts minutes apart all have the same result. – the-baby-is-you Dec 07 '20 at 06:57
  • 2
    `rand` is far from the best random number generator. It can be pretty damn bad, actually. [Here is a presentation on how bad and some better alternatives.](https://channel9.msdn.com/Events/GoingNative/2013/rand-Considered-Harmful) – user4581301 Dec 07 '20 at 07:05
  • Please modify the line `srand(time(0));` to `srand(time(0)); cout << time(0) << " ";` and try to run the program several times. Post new outputs. – 273K Dec 07 '20 at 07:12
  • Why you're not using [``](https://en.cppreference.com/w/cpp/header/random) is the real question here. It is the cat's whiskers, and I *strongly* encourage what it offers. – WhozCraig Dec 07 '20 at 07:32
  • 2
    @S.M. New outputs: 1607326196 63 77 56 91 1 78 52; 1607326199 14 3 69 81 8 36 0; 1607326201 14 94 24 75 53 69 88; 1607326203 14 88 44 69 4 10 84; 1607326204 63 85 6 67 87 27 36 – the-baby-is-you Dec 07 '20 at 07:34
  • Looks good. It seems the C runtime library in your Mac is broken. – 273K Dec 07 '20 at 07:45
  • My curiosity is getting to me. When you [run this](https://godbolt.org/z/brKx4q) ? I concur with Nathan's approach, btw. Just put together your letter "bag", shuffle it, and iterate over it to get your random sequence without replacement. – WhozCraig Dec 07 '20 at 07:48
  • @WhozCraig Compile error. Does it compile for you? – the-baby-is-you Dec 07 '20 at 07:53
  • 1
    You might consider simply throwing away the first few values returned from `rand()`. After seeding with `srand()` use a loop to call `rand()` a few times, hopefully mixing its internal state so you don't have this problem. Ideally you should switch to using the much better functions in the C++ `` header. – Blastfurnace Dec 07 '20 at 08:59
  • Alternative to your algorithm is to `std::shuffle` your letters, and then picks the first ones. – Jarod42 Dec 07 '20 at 09:18
  • @the-baby-is-you It not only compiles for me, it compiles on the site I linked it to you with. Pretty good chance you're either not using a C++11 compatible toolchain, or you are, but don't have the proper language level set (-std=c++11 for g++ and clang++). – WhozCraig Dec 07 '20 at 09:19
  • 2
    I think this is just macOS having a particularly bad rand(). There are a few examples online of people with similar complaints. I'd switch up your seed or use a better generator. https://stackoverflow.com/a/44742394/4386278 https://stackoverflow.com/a/46879475/4386278 https://stackoverflow.com/a/46879475/4386278 – Asteroids With Wings Dec 07 '20 at 11:03
  • Was this wrapped in a script that ran the program a lot of times in a loop (or in succession)? – Spencer Oct 05 '22 at 20:00

2 Answers2

1

As has been explained in comments, it looks like your compiler's C runtime library has a bad rand function.

But you're not using C, you're using C++! Starting at C++11, you have all sorts of random-number generation facilities available in the C++ standard library.

#include <iostream>
#include <string>
#include <random>

int main()
{
    std::random_device eng; // or any other type of engine
    using dist_params = typename std::uniform_int_distribution<int>::param_type;
    int max = 99;
    std::uniform_int_distribution<int> dist (0, max);
    for (int a = 0; a < 7; a++)
    {
        int i = dist(eng);
        std::cout << i << ' ';
        dist.param(dist_params{0, max});
    }
    std::cout << '\n';
    return 0;
}

Or, what I expect you were really going for:

#include <iostream>
#include <string>
#include <random>
#include <time.h>

int main()
{
    std::string bag0 = "AAAAAAAAABBCCDDDDEEEEEEEEEEEEFFGGGHHIIIIIIIIIJKLLLLMMNNNNNNOOOOOOOOPPQRRRRRRSSSSTTTTTTUUUUVVWWXYYZ";

    std::random_device eng;
    time_t t;
    using dist_params = typename std::uniform_int_distribution<size_t>::param_type;
    std::uniform_int_distribution<size_t> dist;
    for (auto j = 0; j<100; ++j)
    {
        auto bag = bag0;
        for (int a = 0; a < 7; a++)
        {
            dist.param(dist_params{0, (bag.length())-1});
            int i = dist(eng);
            std::cout << bag[i] << ' ';
            bag.erase(i, 1);
        }
        std::cout << '\n';
    }
    return 0;
}

The only caveat is that random_device may not produce random numbers on your platform.

Spencer
  • 1,924
  • 15
  • 27
-3

rand() or std::rand() never generates true random number. It generates pseudo-random numbers. This is because computers are unable to generate truly random numbers itself, it requires assistance. Let's say you pressed a key exactly 2.054 seconds after the previous keypress. This is truly a random number. Computers use this data to generate truly random numbers. rand() or std::rand() generates a pseudo-random number, so needs to be seeded (with srand() or std::srand()). If the number you used to seed isn't random, the output wouldn't be random too. Moreover, you are using time() (or std::time()) which returns an int holding the number of seconds passed since epoch. So if you execute the program multiple times too rapidly, the seed would be the same and the output too. It also seems that your standard library a bad rand() or std::rand() function.

Example:

Output of the program (compiled from your code) executed 10 times rapidly (environment: Ubuntu, bash):

$ for i in {0..9} ; do ./a.out ; done
50 11 3 60 36 17 42 
50 11 3 60 36 17 42 
50 11 3 60 36 17 42 
50 11 3 60 36 17 42 
50 11 3 60 36 17 42 
50 11 3 60 36 17 42 
50 11 3 60 36 17 42 
50 11 3 60 36 17 42 
50 11 3 60 36 17 42 
50 11 3 60 36 17 42

What to do?

I can suggest you use another time function to seed, which outputs time in milliseconds (or even nanoseconds) or get your own random number generator. See this article to know how pseudo-random number generators work. This will also help you to build your own as it seems that your standard library gives a bad rand() or std::rand() function.

Akib Azmain Turja
  • 1,142
  • 7
  • 27