Othmane Qadiri
Othmane Qadiri

Reputation: 31

How to generate random numbers in C++ without using time as a seed

My program is supposed to generate random number with a fixed maximum of 9999. These numbers are used to create objects. Now the problem is when using time as a seed, if I create two objects one after the other both will be created with the same number (as the time hasn't changed in the process).

How can I do that more efficiently so I can generate a distinct random number every time I run the program?

Upvotes: 2

Views: 9250

Answers (3)

D.Zadravec
D.Zadravec

Reputation: 647

A simple C++ class to generate random numbers. The header file.

#include <stdio.h>
#include <tchar.h>

#include <random>  
#include <iostream>
#include <algorithm>
#include <vector> 

namespace Random
{
    /// <summary>
    /// Secure pseudo random number generator.
    /// </summary>
    class RandomGenerator
    {
    public:
        /// <summary>
        /// Random number generator.
        /// </summary>
        RandomGenerator();

        /// <summary>
        /// Random number generator destructor.
        /// </summary>
        ~RandomGenerator();

        /// <summary>
        /// Get the randomly ordered numbers between the lower bound and upper bound inclusive. Each random value only occurs once.
        /// </summary>
        /// <param name="lowerBound">The smallest random value to generate.</param>
        /// <param name="upperBound">The largest random value to generate.</param>
        /// <returns>The list of randomly ordered values.</returns>
        /// <exception cref="std::invalid_argument">Invalid parameters.</exception>
        std::vector<int> GetRandomList(int lowerBound = 1, int upperBound = 52);

        /// <summary>
        /// Get the random number between the lower bound and upper bound inclusive.
        /// </summary>
        /// <param name="lowerBound">The smallest random value to generate.</param>
        /// <param name="upperBound">The largest random value to generate.</param>
        /// <returns>A random value.</returns>
        /// <exception cref="std::invalid_argument">Invalid parameters.</exception>
        int Generate(int lowerBound, int upperBound);

    private:
        bool _disposed;
    };
}

And the code file.

#include "RandomGenerator.h"

using namespace Random;

/// <summary>
/// Random number generator.
/// </summary>
RandomGenerator::RandomGenerator() : _disposed(false)
{
}

/// <summary>
/// Random number generator destructor.
/// </summary>
RandomGenerator::~RandomGenerator()
{
    // If not disposed.
    if (!_disposed)
    {
         // Indicate that dispose has been called.
         _disposed = true;
    }
}

/// <summary>
/// Get the randomly ordered numbers between the lower bound and upper bound inclusive. Each random value only occurs once.
/// </summary>
/// <param name="lowerBound">The smallest random value to generate.</param>
/// <param name="upperBound">The largest random value to generate.</param>
/// <returns>The list of randomly ordered values.</returns>
/// <exception cref="std::invalid_argument">Invalid parameters.</exception>
std::vector<int> RandomGenerator::GetRandomList(int lowerBound, int upperBound)
{
    // Validate input.
    if (lowerBound > upperBound)
          throw std::invalid_argument("The parameters are invalid, the lower bound can not be larger then the upper bound.");

    std::vector<int> numbers;
    int arraySize = upperBound - lowerBound + 1;

    // Allocate the total number of values.
    numbers.resize(arraySize);

    // For each value in the bounds.
    for (int i = lowerBound; i <= upperBound; i++)
    {
         // Assign the numbers.
         numbers[i - lowerBound] = i;
    }

    // Non-deterministic generator
    // to seed mersenne twister.
    // Random values on each execution.
    std::random_device rd;
    std::mt19937 engine(rd());

    // Shuffle the numbers between the lower and upper bounds.
    std::shuffle(numbers.begin(), numbers.end(), engine);

    // Return the randomly ordered numbers.
    return numbers;
}

/// <summary>
/// Get the random number between the lower bound and upper bound inclusive.
/// </summary>
/// <param name="lowerBound">The smallest random value to generate.</param>
/// <param name="upperBound">The largest random value to generate.</param>
/// <returns>A random value.</returns>
/// <exception cref="std::invalid_argument">Invalid parameters.</exception>
int RandomGenerator::Generate(int lowerBound, int upperBound)
{
    // Validate input.
    if (lowerBound > upperBound)
         throw std::invalid_argument("The parameters are invalid, the lower bound can not be larger then the upper bound.");

    int randomValue;

    // Non-deterministic generator
    // to seed mersenne twister.
    // Random values on each execution.
    std::random_device rd;
    std::mt19937 engine(rd());

    // Use the uniform distribution to generate
    // a value between lower and upper bound inclusive.
    std::uniform_int_distribution<> dist(lowerBound, upperBound);

    // Generate the random value.
    randomValue = dist(engine);

    // Return the random value.
    return randomValue;
}

Upvotes: 1

Martin
Martin

Reputation: 117

For better understanding I show you how linear congruential generators works.

For this type of generators the math is really simple:

xi+1=(A*xi+B) mod N

Where A, B and N are constants.

And the code could be:

static unsigned int x;  //the seed (time in your case)

unsigned int myRand(){
    x=A*x+B;    //We do not need modulo because it is implicit. For 32b integer the modulo will be 2^32.
    return x;
}

So when you setting x=time, when time doesn't change, you just simply resetting the generator.

So the solution for your problem could be:

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

int main() {
    srand(time(NULL));
    for (unsigned int i=0; i<10;i++){
        std::cout << random()%(9999+1)<<std::endl;
    }
}

Or try this different approach:

#include <iostream>
#include <random>

int main() {
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_int_distribution<int> distribution(0,9999);

    for (unsigned int i=0; i<10;i++){
            std::cout << distribution(gen)<<std::endl;
    }
}

More about random_device: http://www.cplusplus.com/reference/random/random_device/

Upvotes: 0

oleksii
oleksii

Reputation: 35905

The key is - create a random object (or its state) only once and then reuse it to get the next value. This approach is commonly used in other languages, for example, Java, C#, Python.

For an older C-style example see rand, this is the bit you are interested in

//initialize random seed ONCE
srand (time(NULL));

// call MANY TIMES for next value
rand();
rand();
rand();

The same idea with C++11, for example with uniform distribution

// Setup ONCE
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> dis(1, 9999);

// Call MANY TIMES to get next random number 
dis(gen);
dis(gen);
dis(gen);

Upvotes: 9

Related Questions