Reputation: 31
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
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
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
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