Asked  6 Months ago    Answers:  5   Viewed   38 times

I'm trying to make a game with dice, and I need to have random numbers in it (to simulate the sides of the die. I know how to make it between 1 and 6). Using

#include <cstdlib> 
#include <ctime> 
#include <iostream>

using namespace std;

int main() 
{ 
    srand((unsigned)time(0)); 
    int i;
    i = (rand()%6)+1; 
    cout << i << "n"; 
}

doesn't work very well, because when I run the program a few times, here's the output I get:

6
1
1
1
1
1
2
2
2
2
5
2

So I want a command that will generate a different random number each time, not the same one 5 times in a row. Is there a command that will do this?

 Answers

92

The most fundamental problem of your test application is that you call srand once and then call rand one time and exit.

The whole point of srand function is to initialize the sequence of pseudo-random numbers with a random seed.

It means that if you pass the same value to srand in two different applications (with the same srand/rand implementation) then you will get exactly the same sequence of rand() values read after that in both applications.

However in your example application pseudo-random sequence consists only of one element - the first element of a pseudo-random sequence generated from seed equal to current time of 1 sec precision. What do you expect to see on output then?

Obviously when you happen to run application on the same second - you use the same seed value - thus your result is the same of course (as Martin York already mentioned in a comment to the question).

Actually you should call srand(seed) one time and then call rand() many times and analyze that sequence - it should look random.

AMENDMENT 1 - example code:

OK I get it. Apparently verbal description is not enough (maybe language barrier or something... :) ).

Old-fashioned C code example based on the same srand()/rand()/time() functions that was used in the question:

#include <stdlib.h>
#include <time.h>
#include <stdio.h>

int main(void)
{
    unsigned long j;
    srand( (unsigned)time(NULL) );

    for( j = 0; j < 100500; ++j )
    {
        int n;

        /* skip rand() readings that would make n%6 non-uniformly distributed
          (assuming rand() itself is uniformly distributed from 0 to RAND_MAX) */
        while( ( n = rand() ) > RAND_MAX - (RAND_MAX-5)%6 )
        { /* bad value retrieved so get next one */ }

        printf( "%d,t%dn", n, n % 6 + 1 );
    }

    return 0;
}

^^^ THAT sequence from a single run of the program is supposed to look random.

Please NOTE that I don't recommend to use rand/srand functions in production for the reasons explained below and I absolutely don't recommend to use function time as a random seed for the reasons that IMO already should be quite obvious. Those are fine for educational purposes and to illustrate the point sometimes but for any serious use they are mostly useless.

AMENDMENT 2 - detailed explanation:

It is important to understand that as of now there is none C or C++ standard features (library functions or classes) producing actually random data definitively (i.e. guaranteed by the standard to be actually random). The only standard feature that approaches this problem is std::random_device that unfortunately still does not provide guarantees of actual randomness.

Depending on the nature of application you should first decide if you really need truly random (unpredictable) data. Notable case when you do most certainly need true randomness is information security - e.g. generating symmetric keys, asymmetric private keys, salt values, security tokens, etc.

However security-grade random numbers is a separate industry worth a separate article.

In most cases Pseudo-Random Number Generator is sufficient - e.g. for scientific simulations or games. In some cases consistently defined pseudo-random sequence is even required - e.g. in games you may choose to generate exactly same maps in runtime to avoid storing lots of data in your distribution.

The original question and reoccurring multitude of identical/similar questions (and even many misguided "answers" to them) indicate that first and foremost it is important to distinguish random numbers from pseudo-random numbers AND to understand what is pseudo-random number sequence in the first place AND to realize that pseudo-random number generators are NOT used the same way you could use true random number generators.

Intuitively when you request random number - the result returned shouldn't depend on previously returned values and shouldn't depend if anyone requested anything before and shouldn't depend in what moment and by what process and on what computer and from what generator and in what galaxy it was requested. That is what word "random" means after all - being unpredictable and independent of anything - otherwise it is not random anymore, right? With this intuition it is only natural to search the web for some magic spells to cast to get such random number in any possible context.

^^^ THAT kind of intuitive expectations IS VERY WRONG and harmful in all cases involving Pseudo-Random Number Generators - despite being reasonable for true random numbers.

While the meaningful notion of "random number" exists (kind of) - there is no such thing as "pseudo-random number". A Pseudo-Random Number Generator actually produces pseudo-random number sequence.

Pseudo-random sequence is in fact always deterministic (predetermined by its algorithm and initial parameters) - i.e. there is actually nothing random about it.

When experts talk about quality of PRNG they actually talk about statistical properties of the generated sequence (and its notable sub-sequences). For example if you combine two high quality PRNGs by using them both in turns - you may produce bad resulting sequence - despite them generating good sequences each separately (those two good sequences may simply correlate to each other and thus combine badly).

Specifically rand()/srand(s) pair of functions provide a singular per-process non-thread-safe(!) pseudo-random number sequence generated with implementation-defined algorithm. Function rand() produces values in range [0, RAND_MAX].

Quote from C11 standard (ISO/IEC 9899:2011):

The srand function uses the argument as a seed for a new sequence of pseudo-random numbers to be returned by subsequent calls to rand. If srand is then called with the same seed value, the sequence of pseudo-random numbers shall be repeated. If rand is called before any calls to srand have been made, the same sequence shall be generated as when srand is first called with a seed value of 1.

Many people reasonably expect that rand() would produce a sequence of semi-independent uniformly distributed numbers in range 0 to RAND_MAX. Well it most certainly should (otherwise it's useless) but unfortunately not only standard doesn't require that - there is even explicit disclaimer that states "there is no guarantees as to the quality of the random sequence produced". In some historical cases rand/srand implementation was of very bad quality indeed. Even though in modern implementations it is most likely good enough - but the trust is broken and not easy to recover. Besides its non-thread-safe nature makes its safe usage in multi-threaded applications tricky and limited (still possible - you may just use them from one dedicated thread).

New class template std::mersenne_twister_engine<> (and its convenience typedefs - std::mt19937/std::mt19937_64 with good template parameters combination) provides per-object pseudo-random number generator defined in C++11 standard. With the same template parameters and the same initialization parameters different objects will generate exactly the same per-object output sequence on any computer in any application built with C++11 compliant standard library. The advantage of this class is its predictably high quality output sequence and full consistency across implementations.

Also there are more PRNG engines defined in C++11 standard - std::linear_congruential_engine<> (historically used as fair quality srand/rand algorithm in some C standard library implementations) and std::subtract_with_carry_engine<>. They also generate fully defined parameter-dependent per-object output sequences.

Modern day C++11 example replacement for the obsolete C code above:

#include <iostream>
#include <chrono>
#include <random>

int main()
{
    std::random_device rd;
    // seed value is designed specifically to make initialization
    // parameters of std::mt19937 (instance of std::mersenne_twister_engine<>)
    // different across executions of application
    std::mt19937::result_type seed = rd() ^ (
            (std::mt19937::result_type)
            std::chrono::duration_cast<std::chrono::seconds>(
                std::chrono::system_clock::now().time_since_epoch()
                ).count() +
            (std::mt19937::result_type)
            std::chrono::duration_cast<std::chrono::microseconds>(
                std::chrono::high_resolution_clock::now().time_since_epoch()
                ).count() );

    std::mt19937 gen(seed);

    for( unsigned long j = 0; j < 100500; ++j )
    /* ^^^Yes. Generating single pseudo-random number makes no sense
       even if you use std::mersenne_twister_engine instead of rand()
       and even when your seed quality is much better than time(NULL) */    
    {
        std::mt19937::result_type n;
        // reject readings that would make n%6 non-uniformly distributed
        while( ( n = gen() ) > std::mt19937::max() -
                                    ( std::mt19937::max() - 5 )%6 )
        { /* bad value retrieved so get next one */ }

        std::cout << n << 't' << n % 6 + 1 << 'n';
    }

    return 0;
}

The version of previous code that uses std::uniform_int_distribution<>

#include <iostream>
#include <chrono>
#include <random>

int main()
{
    std::random_device rd;
    std::mt19937::result_type seed = rd() ^ (
            (std::mt19937::result_type)
            std::chrono::duration_cast<std::chrono::seconds>(
                std::chrono::system_clock::now().time_since_epoch()
                ).count() +
            (std::mt19937::result_type)
            std::chrono::duration_cast<std::chrono::microseconds>(
                std::chrono::high_resolution_clock::now().time_since_epoch()
                ).count() );

    std::mt19937 gen(seed);
    std::uniform_int_distribution<unsigned> distrib(1, 6);

    for( unsigned long j = 0; j < 100500; ++j )
    {
        std::cout << distrib(gen) << ' ';
    }

    std::cout << 'n';
    return 0;
}
Tuesday, June 1, 2021
 
Keat
answered 6 Months ago
36

Every time you do new Random() it is initialized using the clock. This means that in a tight loop you get the same value lots of times. You should keep a single Random instance and keep using Next on the same instance.

//Function to get a random number 
private static readonly Random random = new Random(); 
private static readonly object syncLock = new object(); 
public static int RandomNumber(int min, int max)
{
    lock(syncLock) { // synchronize
        return random.Next(min, max);
    }
}

Edit (see comments): why do we need a lock here?

Basically, Next is going to change the internal state of the Random instance. If we do that at the same time from multiple threads, you could argue "we've just made the outcome even more random", but what we are actually doing is potentially breaking the internal implementation, and we could also start getting the same numbers from different threads, which might be a problem - and might not. The guarantee of what happens internally is the bigger issue, though; since Random does not make any guarantees of thread-safety. Thus there are two valid approaches:

  • Synchronize so that we don't access it at the same time from different threads
  • Use different Random instances per thread

Either can be fine; but mutexing a single instance from multiple callers at the same time is just asking for trouble.

The lock achieves the first (and simpler) of these approaches; however, another approach might be:

private static readonly ThreadLocal<Random> appRandom
     = new ThreadLocal<Random>(() => new Random());

this is then per-thread, so you don't need to synchronize.

Tuesday, June 1, 2021
 
twk
answered 6 Months ago
twk
81

Note: Don't use rand() for security. If you need a cryptographically secure number, see this answer instead.

#include <time.h>
#include <stdlib.h>

srand(time(NULL));   // Initialization, should only be called once.
int r = rand();      // Returns a pseudo-random integer between 0 and RAND_MAX.

On Linux, you might prefer to use random and srandom.

Tuesday, June 1, 2021
 
Naveen
answered 6 Months ago
45

You need to seed the psuedo-random number generator first using srand.

#include <algorithm>
#include <cstdlib>

...

std::srand(std::time(0));

vector <Card> deck;
//some code to add cards to the deck here
random_shuffle ( deck.begin(), deck.end() );

Note from link above:

Generally speaking, the pseudo-random number generator should only be seeded once, before any calls to rand(), and the start of the program. It should not be repeatedly seeded, or reseeded every time you wish to generate a new batch of pseudo-random numbers.

Friday, August 6, 2021
 
Amalina Aziz
answered 4 Months ago
36

First off: there is nothing wrong with calling non-virtual methods in the constructor. Where did you read that there was? (Note: calling virtual methods can be a problem; it is not an automatic no-no, but you need to watch what you are doing very carefully).

As an aside, it seems wasteful to generate a new Random instance every time GenerateRandomNumber is called. You can extract the Random instance to a field to fix that:

class RandomNumberHandler
{
    private readonly Random random = new Random();
    private int randomNumber;

    public RandomNumberHandler()
    {
        this.randomNumber = GenerateRandomNumber();
    }

    private int GenerateRandomNumber()
    {
        return this.random.Next(3000) + 1000;
    }
}

But this raises another question: if GenerateRandomNumber is only called once in each instance's lifetime (in the constructor), then it doesn't make sense to create a new Random for each object. So the next logical step is to make random be static. This means that GenerateRandomNumber can also become static (and indeed, it has to):

class RandomNumberHandler
{
    private static readonly Random Random = new Random();
    private int randomNumber;

    public RandomNumberHandler()
    {
        this.randomNumber = GenerateRandomNumber();
    }

    private static int GenerateRandomNumber()
    {
        return Random.Next(3000) + 1000;
    }
}
Monday, October 25, 2021
 
Emmanuel Gleizer
answered 1 Month ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :
 
Share