I've a function to generate random characters from a set of alphabets. This function would be called multiple times and thus I'm trying to make it use same set of variables, ie have same seed so that strings don't repeat as long as possible.
#include <iostream>
#include <random>
#include <string>
std::string generateRandomChar(const unsigned int _len)
{
std::string result;
result.reserve(_len);
static constexpr char alphanum[] = "0123456789"
"abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> dis(0, 61);
for (int i = 0; i < _len;
result += (alphanum[dis(gen)]);
}
return result;
}
int main(){
for(int i = 0; i < 10; ++i){
std::cout << generateRandomChar(10) << std::endl;
}
}
Unfortunately I don't have any expertise with c++11 functions and I was only using srand and friends earlier, so I might be making lots of mistakes here. Currently it works and generates tons of strings without repeating, but I'm sure I could make either of -
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> dis(0, 61);
static variable too, so that it isn't calculated each time the function is called because that would be waste right?
So which one should be static here? Are there any mistakes/improvements that you see here?
Thankyou :D
edit - here's an ideone link - http://ideone.com/e7ssXo
So which one should be static here?
Either std::mt19937 gen or none of them.
std::random_device generates uniformly-distributed random integers using non-deterministic entropy source, if available. Your code uses std::random_device output as a seed for std::mt19937 pseudo-random generator. So you are very likely to have good, non-deterministic seeds for std::mt19937 if you run on a PC, and thus there is no need to make any of them static. I suppose that std::random_device is implemented as a pure-procedural PRNG only on low-end controllers.
On the other hand, one-time seeding is more idiomatic, so you may declare std::mt19937 as static. std::mt19937 itself implements a high-quality PRNG algorithm with 19937 bits of state, so it's period probably exceeds the Universe lifetime (or at least the stelliferous era), if the generator is running on a modern PC.
There is no need to make std::uniform_int_distribution static, as it's only a wrapper which modifies actual generator's output according to desired distribution properties. So it's up to you to decide whether make it static or not.
To allow unit test, I suggest to move the generator outside of the function, something like
std::string generateRandomChar(const unsigned int len, std::mt19937& gen)
{
std::string result;
result.reserve(len);
static constexpr char alphanum[] = "0123456789"
"abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
std::uniform_int_distribution<> dis(0, 61);
for (int i = 0; i < len; ++i) {
result += (alphanum[dis(gen)]);
}
return result;
}
int main() {
std::random_device rd;
std::mt19937 gen(rd());
for (int i = 0; i < 10; ++i) {
std::cout << generateRandomChar(10, gen) << std::endl;
}
}
or create a class
class RandomCharGenerator
{
public:
RandomCharGenerator() : RandomCharGenerator(std::random_device{}()) {}
template <typename T>
RandomCharGenerator(T&& seed) : gen(std::forward<T>(seed)) {}
std::string operator() (const unsigned int len)
{
std::string result;
result.reserve(len);
for (int i = 0; i < len; ++i) {
result += (alphanum[dis(gen)]);
}
return result;
}
private:
std::mt19937 gen;
std::uniform_int_distribution<> dis{0, 61};
static constexpr char alphanum[] = "0123456789"
"abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
};
int main() {
RandomCharGenerator charGenerator{};
for (int i = 0; i < 10; ++i) {
std::cout << charGenerator(10) << std::endl;
}
}
You can eventually templated things to allow other rand generator.
You can make your random_device and mt19937 static. Currently you are constructing one everytime the function is called (IIRC, mt19937 is expensive to construct. Someone corect me). Also, your uniform_int_distribution can be moved out of the loop. This question is better suited for codereview as it already works and you are looking for improvements.
TL;DR: You can make all of them static.
Related
I'm new to C++ and I am trying to create a basic genetic algorithm. I created a Chromosome class and want to create a Society class that generates a vector of these Chromosomes with randomly generated "genes". Genes being the vector in the Chromosome that holds values of 0 or 1. I was testing out the Chromosome constructor, and all of the objects have the same gene vectors. How can I make the constructor generate random values? I have included code below. Any other coding practice or optimization tips would also be extremely appreciated.
Source.cpp
#include "Chromosome.h"
#include "Society.h"
using namespace std;
int main()
{
Chromosome demo = Chromosome::Chromosome();
Chromosome demo2 = Chromosome::Chromosome();
return 1;
}
Chromosome.h
#pragma once
#include <vector>
using namespace std;
class Chromosome
{
private:
int fitness;
vector<int> genes;
public:
Chromosome();
void generateGenes();
int calculateFitness(),
getFitness();
vector<int> getGenes();
void setGenes(vector<int> child);
};
Chromosome.cpp
#include "Chromosome.h"
#include <cstdlib>
#include <ctime>
#include <numeric>
using namespace std;
Chromosome::Chromosome()
{
generateGenes();
Chromosome::fitness = calculateFitness();
}
void Chromosome::generateGenes()
{
srand(time(NULL));
for (unsigned i = 0; i < 10; i++)
{
unsigned chance = rand() % 5;
Chromosome::genes.push_back((!chance)? 1 : 0);
}
}
int Chromosome::calculateFitness()
{
int sum = 0;
for (unsigned i = 0; i < Chromosome::genes.size(); i++)
{
sum += Chromosome::genes[i];
}
return sum;
}
int Chromosome::getFitness()
{
return Chromosome::fitness;
}
vector<int> Chromosome::getGenes()
{
return Chromosome::genes;
}
void Chromosome::setGenes(vector<int> child)
{
Chromosome::genes = child;
}
You seed the random number generator with the same value time(NULL).
Two calls after eachother will return the same time_t. You'll generate one set of random numbers first, then reset the random number generator and generate them again.
Only call srand() once during the whole program run.
Also, use <random> instead to get better/faster random number generators.
Instead of rand() % 5; using <random>:
#include <random>
// A function to return a random number generator.
inline std::mt19937& generator() {
// the generator will only be seeded once since it's static
static std::mt19937 gen(std::random_device{}());
return gen;
}
// A function to generate unsigned int:s in the range [min, max]
int my_rand(unsigned min, unsigned max) {
std::uniform_int_distribution<unsigned > dist(min, max);
return dist(generator());
}
Then call it:
unsigned chance = my_rand(0, 4);
Your problem is the use of rand & srand in a C++ program.
srand(time(NULL));
unsigned chance = rand() % 5;
in this implementation, rand might return multiple numbers that will give you the same final result. for example:
19, 24, 190214, 49789, 1645879, 15623454, 4, 156489719, 1645234, 152349, ...
There are different ways of generate random numbers in C++, this one isn't recommended due to bad results.
One (of many) good ways to generate random, using "pseudo-random" in C++:
void Chromosome::generateGenes()
{
// Initialize random
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(0, 5);
for (unsigned i = 0; i < 10; i++)
{
// Use random: dis(gen);
unsigned chance = dis(gen);
Chromosome::genes.push_back((!chance)? 1 : 0);
}
}
Include:
#include <random>
Right note by #TedLyngmo: Every time that function will be called (in your case, in every object creation in the constructor call), this code will make you generate a new random seed (In 'Initialize random' section). In more progress cases, or as the program grows, it is highly recommended to extract this initialize to another function (and maybe to a new class object for modular programming reason). In this response I demonstrated the general syntax of using this type of random in your case.
Read about:
Pseudo-random number generation
Uniform Distribution
Thanks to #M.M: How to succinctly, portably, and thoroughly seed the mt19937 PRNG?
I would like to use built-in distribution, but add some constraints to it. I tried something like this, but i get same number when using function. How can i avoid this? Can i use distribution as a argument to the function?
double Cauchy(double Fm){
std::default_random_engine generator;
std::cauchy_distribution<double> distribution(Fm, 0.1);
double number=distribution(generator);
while(number<0)
number=distribution(generator);
if (number>1)
number = 1;
return number;
}
Now i changed function and it looks like this
double Cauchy_1(double Fm, std::random_device &rd){
std::default_random_engine generator(rd());
std::cauchy_distribution<double> distribution(Fm, 0.1);
double number=distribution(generator);
while(number<0)
number=distribution(generator);
if (number>1)
number =1;
return number;
}
std::random_device rd;
int i=15;
double Crm=0.1, num;
while (i>0){
num=Cauchy_1(0.1, rd);
cout<<num<<endl;
i--;
}
It gives me different values, but values are the same on new run.
You initialize std::default_random_engine generator; with the same default seed. You need to seed it to get different outputs if you instantiate it anew. There is std::random_device class you can use to get a new random seed.
Also, std::default_random_engine is slow class to instantiate/create so you are using it wrong.
Functions in the standard library like std::shuffle take a random number generator by a forwarding reference, not a distribution. You can do the same:
template<class URBG>
double cauchy_1(double fm, URBG&& gen) {
std::cauchy_distribution<double> distribution(fm, 0.1);
double number;
do
number = distribution(gen);
while (number < 0);
return std::min(number, 1.0);
}
int main() {
std::random_device rd;
std::default_random_engine gen(rd());
for (int i = 0; i < 10; ++i) {
auto num = cauchy_1(0.1, gen);
std::cout << num << std::endl;
}
}
It still has same set of numbers if i rerun this code.
This is not the problem of this code, but the problem of std::random_device. As explained here, std::random_device may be implemented in terms of a pseudo-random number engine. Possible solutions can be found here.
For example:
std::default_random_engine gen(
std::chrono::system_clock::now().time_since_epoch().count());
string bolsa_letras::letters_generator(int quantity){
int already_generated = 0;
map<char, int> aux = values;
string out;
while(already_generated != quantity){
char generated_char = 'A' + rand()%26;
if(aux[generated_char] > 0){
out.push_back(generated_char);
aux[generated_char]--;
already_generated++;
}
}
return out;
}
Above is the code that given a number generates random letters.
The map saves the letters and the times that letters can be appeared. The problem is that every time i run the code, it prints the same: NLRBBMQH. Why is so?
I have include cstdlib for the rand function.
A deterministic program cannot naturally generate randomness. We need to get that randomness from elsewhere. In the old days of rand(), we'd generally seed the RNG with the result of std::time(NULL) to produce a different state at each run.
Nowadays, we use the tools provided by <random> which are "more random".
From cppreference.com's documentation on std::uniform_int_distribution you can find an example for integers of type int. Now, char is an integer type too. Let us tweak their example a tiny bit:
#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<char> dis('A', 'Z');
for (int n=0; n<10; ++n) {
std::cout << dis(gen) << ' ';
}
std::cout << '\n';
}
And tadaaaa! a random letter generator :)
Live demo
For my program, I needed so far to draw one random value in [0..k[ from time to time, and using C++11 <random> features works really well. My current code is something like
class Random
{
public:
Random() : rng( rd() ) { }
inline int getRandNum( int limit ) { return ( numbers(rng) % limit ); }
private:
std::random_device rd;
std::mt19937 rng;
std::uniform_int_distribution<int> numbers;
};
Now, I need to draw in a row n different values in [0..k[. I was looking for something in <random> allowing that, but either I am not able to find it, or such a thing does not exist yet. Is there a clever, more elegant way to proceed than calling my getRandNum function and repeat until I get n different values?
EDIT: to give an idea, in my program k is some thousands and n some tens.
This solution is not C++ specific but can be easily implemented in any language.
What you want is essentially shuffle numbers 0 to k and pick the first n numbers, where n <= k.
This can be done using a reservoir sampling algorithm. See this wikipedia link for the pseudocode.
Note that it is possible to get the n numbers without storing all k numbers and shuffling them. That is, it is possible to just use O(n) space, where n is the number of random numbers you wish to obtain, instead of O(k). The time complexity for this algorithm is O(k), if we assume generating the random number takes O(1) time.
If k is several thousands and n is tens, then a permutation generation is really not the best choise. But calling getRandNum is not what you want too, because it can return the same value several times.
One option is to generate random sequence all at once, checking that the numbers don't repeat. The easiest (and may be even the most efficient) way to achieve this is to use a set.
Like so:
#include <vector>
#include <set>
#include <iostream>
#include <random>
class Random
{
public:
Random() : rng( rd() ) { }
inline int getRandNum( int limit ) { return ( numbers(rng) % limit ); }
std::set<int> getRandSequence(int limit, int n);
private:
std::random_device rd;
std::mt19937 rng;
std::uniform_int_distribution<int> numbers;
};
std::set<int> Random::getRandSequence(int limit, int n)
{
std::set<int> generatedSequence;
while (generatedSequence.size() < n) //size() for set is O(1) if I'm not mistaken
generatedSequence.insert(getRandNum(limit));
return generatedSequence;
}
int main()
{
Random r;
auto sequence = r.getRandSequence(1000, 10);
std::cout << "Seq;uence: " << std::endl;
for (int number : sequence)
std::cout << number << std::endl;
std::cout << "End" << std::endl;
return 0;
}
Ideone demo.
By the way, random_device creation is expensive, but uniform_int_distribution creation, as far as I remember, is not. So this might be even more efficient:
std::set<int> Random::getRandSequence(int limit, int n)
{
std::uniform_int_distribution<int> uiniformDistribution(0, limit);
std::set<int> generatedSequence;
while (generatedSequence.size() < n)
generatedSequence.insert(uiniformDistribution(rng));
return generatedSequence;
}
Besides, when you get a uniform distribution and then apply % limit to it, you don't get a uniform distribution anymore.
std::random_device rd; // obtain a random number from hardware
std::mt19937 eng(rd()); // seed the generator
std::uniform_int_distribution<> distr(0, 1500); // define the range
for(int a=0; a<limit; a++){
cout << distr(eng); //draw random nubmer
I need to generate a random number between 1 and n where n is unsigned int.
If n were int I would simply write 1 + rand()% n. But unfortunately n is unsigned int. What do you suggest?
rand() should be avoided whenever possible*.
Use http://en.cppreference.com/w/cpp/numeric/random
#include <random>
#include <iostream>
int main()
{
std::random_device rd;
std::mt19937 engine(rd());
std::uniform_int_distribution<unsigned> dist(1, 77);
for (int i = 0; i != 5; ++i)
std::cout << dist(engine) << '\n';
}
* Because it shares a global state, gets often implemented as a linear congruential engine which has a few drawbacks and it's range is often only 0-2^16. Also, % n where n is not an exact multiple of the range does not produce an uniform distribution.
Edit: This might seem like overkill, but technically one would want something like this, since mt19937 needs a bit of "warm up":
std::mt19937 create_seeded_rng()
{
std::random_device rd;
std::array<std::mt19937::result_type, std::mt19937::state_size> seed_data;
std::generate(seed_data.begin(), seed_data.end(), std::ref(rd));
std::seed_seq seq(seed_data.begin(), seed_data.end());
return std::mt19937(seq);
}
int main()
{
std::mt19937 rng = create_seeded_rng();
std::uniform_int_distribution<int> dist(0, 100);
for (unsigned i = 0; i != 100; ++i)
std::cout << dist(rng) << '\n';
}