What is the most efficient way to generate random strings in C++? - c++

I need to generate random strings efficiently. In the following, you will see my first try. I compiled the code with gcc and -O3 optimization level. It takes 18.5 seconds to generate 10^7 random strings of length 64:
#include <iostream>
#include <random>
#include <algorithm>
std::string chars {"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!##$%^&*()`~-_=+[{]{|;:'\",<.>/?"};
std::random_device rd;
std::mt19937 generator(rd());
std::string rand_str (int length) {
std::string output (chars);
std::shuffle(output.begin(), output.end(), generator);
return output.substr(0, length);
}
int main() {
std::string str;
for (long i=0; i<10000000; ++i)
str = rand_str (64);
}
I checked std::sample in c++17 and it is not faster than the above method. In addition, it will not change the order of characters and so it is not really random.
Edit: The std::shuffle is not a good choice, since, it will not allow duplicates. Based on comments I modified the code. This time it takes more than 9 minutes for 10^7 random numbers.
std::string rand_str (size_t length) {
const size_t char_size = chars.size();
std::uniform_int_distribution<> random_int (0, char_size - 1);
std::string output;
for (size_t i=0; i<length; ++i)
output.push_back(chars[random_int(generator)]);
return output;
}
Question
Are there more efficient ways to do this in modern C++?
I appreciate any suggestions to improve the code.

#include <iostream>
#include <random>
#include <algorithm>
#include <chrono>
std::string chars {"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!##$%^&*()`~-_=+[{]{|;:'\",<.>/?"};
std::random_device rd;
std::mt19937 generator(rd());
std::string rand_str(int length) {
std::string output;
output.reserve(length);
while(length>0)
{
auto randNumb = generator();
while(randNumb > 93 && length--)
{
output.push_back(chars[randNumb%93]);
randNumb/=93;
}
}
return output;
}
int main() {
auto startTP = std::chrono::system_clock::now();
std::string rand_bytes;
for (long i=0; i<10000000; ++i)
rand_bytes = std::move(rand_str(64));
auto endTP = std::chrono::system_clock::now();
std::cout << "This took: " << std::chrono::duration_cast<std::chrono::microseconds>(endTP-startTP).count() << std::endl;
}
This takes around 3 seconds on my machine. The trick is to call the random number generator as little as possible and to allocate the memory only once.
What I'm doing is converting randNumber from base 10 to base 93(the length of chars). After that im using every base 93 digit as a different random number. This provides around 5 numbers per generated random number.

Related

How to generate different random number for the same variable

I want to use a while loop to generate a random number for a variable to spell out a scrambled word. My problem is that my code generates a number that is random but repeats that number rather than using a new number.
#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;
int main()
{
string wordList[5] = {"cool", "friend", "helpful", "amazing",
"person"};
srand(time(0));
int rWord = rand() % 5 + 1;
string randWord = wordList[rWord];
int runs = 0;
int wordLen = randWord.length();
while(runs != wordLen){
int ranLN = rand() % wordLen;
char randLetter = randWord[ranLN];
cout << randLetter;
runs++;
}
return 0;
}
I expected my results to be a fully scrambled word, but I instead got repeated letters. For example, I got the word "friend" scrambled as "eennn".
As suggested in comments, the current range of rWord is 1,2,3,4,5 which must be fixed to 0,1,2,3,4.
Thus I removed +1 from it's initialization equation in the following answer.
In addition, ranLN can be duplicate thus you got repeated letters.
Then, a possible way is recursively shuffling all characters of randWord and output them after the while loop finished as follows.
The same algorithm is shown here as an example:
DEMO
#include <iostream>
#include <string>
#include <cstdlib>
#include <ctime>
#include <utility>
int main()
{
std::string wordList[5] = {"cool", "friend", "helpful", "amazing", "person"};
srand(time(0));
std::size_t rWord = rand() % 5;
std::string randWord = wordList[rWord];
std::size_t runs = 0;
std::size_t wordLen = randWord.length();
while(runs != wordLen)
{
std::swap(randWord[runs], randWord[rand() % wordLen]);
++runs;
}
std::cout << randWord << std::endl;
return 0;
}
BTW, although rand() should be usually implemented by a something better LCG,
but, for instance as noted in (my local) C++ standard draft n4687, the algorithm used in rand() is completely compiler implementation defined:
29.6.9 Low-quality random number generation [c.math.rand]
int rand();
void srand(unsigned int seed);
... rand’s underlying algorithm is unspecified. Use of rand therefore continues to be non-portable, with unpredictable and oft-questionable quality and performance.
Fortunately, in C++11 and over, we can use <random> to generate a guaranteed quality randomness.
Thus I recommend you to use them with std::shuffle as follows.
If you need more high-quality randomness, you can use std::mt19937 instead of std::minstd_rand:
DEMO
#include <iostream>
#include <string>
#include <random>
#include <algorithm>
int main()
{
std::string wordList[5] = {"cool", "friend", "helpful", "amazing", "person"};
std::minstd_rand gen(std::random_device{}());
std::uniform_int_distribution<std::size_t> dis(0, 4);
std::size_t rWord = dis(gen);
std::string randWord = wordList[rWord];
std::shuffle(randWord.begin(), randWord.end(), gen);
std::cout << randWord << std::endl;
return 0;
}
In my humble opinion after generating all random words then using set data structure would make the random word unique.

Generate random letters

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

generate a random string in C++11? [duplicate]

This question already has answers here:
How do I create a random alpha-numeric string in C++?
(20 answers)
Closed 4 years ago.
I need help with generating a random string using C++11.
I don't know how to continue with that, if you can help me please.
#include <random>
char * random_string()
{
static const char alphabet[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
static const MAX_LEN = 32; //MAX LENGTH OF THE NEW CHAR RETURNED
int stringLength = sizeof(alphabet)/sizeof(alphabet[0]);
for (int i = 0; i<=MAX_LEN;i++)
{
//now i don't know what i need to do help!
}
static const char test[MAX_LEN];
return test;
}
Return a std::string rather than a raw char *. Populate the string as needed to start, and then shuffle it.
For example;
#include <random>
#include <string>
std::string random_string()
{
std::string str("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
std::random_device rd;
std::mt19937 generator(rd());
std::shuffle(str.begin(), str.end(), generator);
return str.substr(0, 32); // assumes 32 < number of characters in str
}
If you really need to extract a raw const char * from a std::string use its c_str() member function.
int main()
{
std::string rstr = random_string();
some_func_that_needs_const_char_pointer(rstr.c_str());
}
Using Mersene Twister 19937 generator and Uniform discrete distribution you can generate random strings from ranges like "A-Z","a-z" or "0-9" easily.
#include <iostream>
#include <string>
#include <random>
using namespace std;
int main() {
mt19937 generator{random_device{}()};
//modify range according to your need "A-Z","a-z" or "0-9" or whatever you need.
uniform_int_distribution<int> distribution{'a', 'z'};
auto generate_len = 6; //modify length according to your need
string rand_str(generate_len, '\0');
for(auto& dis: rand_str)
dis = distribution(generator);
cout << "Random string generated : " << rand_str << endl;
}
#include <random>
using namespace std;
string generate(int max_length){
string possible_characters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
random_device rd;
mt19937 engine(rd());
uniform_int_distribution<> dist(0, possible_characters.size()-1);
string ret = "";
for(int i = 0; i < max_length; i++){
int random_index = dist(engine); //get index between 0 and possible_characters.size()-1
ret += possible_characters[random_index];
}
return ret;
}

C++ Random number generator apparent malfunction: am I unlucky?

I wanted to try the C++ random number generator but couldn't seem to obtain very satisfying results. For example here is an attempt among others to create a random string of characters.
#include <iostream>
#include <string>
#include <random>
std::string f(unsigned int n){
std::uniform_int_distribution<int> dis025(0, 25);
std::mt19937 gen_mt(n);
std::string str(5, '\0');
for(int i = 0; i<5; i++)
str[i] = (char)('a' + dis025(gen_mt));
return str;
}
int g(unsigned int n, int m){
std::uniform_int_distribution<int> dis(0, m);
std::mt19937 gen_mt(n);
return dis(gen_mt);
}
int main() {
std::string s = f(g(106175305, 40000000)) + " " + f(g(53718209, 40000000));
std::cout << "Random string: " << s << std::endl;
}
Link to Coliru.
(I had to use the f(g()) trick so that it stops shouting insults.)
It is quite annoying and I doubt that is the desired behavior. But somehow I am helpless to prevent it, it keeps happening, again...
#include <iostream>
#include <string>
#include <random>
std::string fx(unsigned int n, int m){
std::uniform_int_distribution<int> dis(0, m);
std::mt19937 gen_mt(n);
std::string str(6, '\0');
for(int i = 0; i<6; i++)
str[i] = (char)('.' + dis(gen_mt));
return str;
}
int g(unsigned int n, int m){
std::uniform_int_distribution<int> dis(0, m);
std::mt19937 gen_mt(n);
return dis(gen_mt);
}
int main() {
std::string s1 = fx(g(66730461, 90000000) + 400000000, 33) + "/" + fx(g(28989020, 90000000) * 10, 43);
std::cout << s1 << std::endl;
}
Coliru.
...and again.
int main() {
std::string s2 = fx(g(66730461, 90000000) + 400000000, 33) + "/" + fx(g(81141643, 90000000) + 100000000, 43);
std::cout << s2 << std::endl;
}
Do you often meet that kind of problem? or am I especially unlucky?
My sad friend, I must inform you that in all my life I have never encountered a programmer with as poor luck as you have. The chances of a random string generator creating human-readable output is one in a million, but you managed to do it three times in a row (one in a trillion?)
In all honesty, the trick was quite clever. May your future endeavors be more predictable :)
(Future readers: The results were generated using hand-picked pseudo-random seed values that happened to output specific strings, like "hello world", etc... Check out the comments on the question for more info)

random function in C++

Is there a function that generates k random numbers within a specified range.
For example I want 5 random numbers between 0 to 100, with or without replacement.
You could use std::generate_n with either rand() or a generator from the new C++11 random number generators.
There is the Boost library, which you can use to generate random numbers, for example.
The following code generates 5 random numbers from [0, 100] with replacement:
#include <vector>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_int_distribution.hpp>
const int numWantedNumbers = 5;
int main()
{
boost::random::mt19937 generator;
boost::random::uniform_int_distribution<> distribution(0, 100);
std::vector<int> result;
for (int i = 0; i < numWantedNumbers; ++i)
result.push_back(distribution(generator));
}
If you want to generate the numbers without replacement, simply check if they are
still available:
#include <algorithm>
#include <vector>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_int_distribution.hpp>
const int numWantedNumbers = 5;
int main()
{
boost::random::mt19937 generator;
boost::random::uniform_int_distribution<> distribution(0, 100);
std::vector<int> result;
while (result.size() < numWantedNumbers)
{
int number = distribution(generator);
if (std::find(result.begin(), result.end(), number) == result.end())
result.push_back(number);
}
}
Note: The rejection sampling in the example without replacement has the obvious drawback that longer vectors are quite difficult to create. Just try to draw 99 out
of 100 numbers, to see what I mean (or even better draw 9999 out of 10000). If this
is a problem, I would suggest to create a random permutation of all possible numbers
and then cut the vector at the requested size:
#include <algorithm>
#include <vector>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_int_distribution.hpp>
const int numWantedNumbers = 5;
int main()
{
boost::random::mt19937 generator;
boost::random::uniform_int_distribution<> distribution(0, 100);
// Generate a vector with all possible numbers and shuffle it.
std::vector<int> result;
for (int i = 0; i <= 100; ++i)
result.push_back(i);
for (int i = 0; i <= 100; ++i)
{
int x = distribution(generator);
std::swap(result[i], result[x]);
}
// Truncate to the requested size.
result.resize(numWantedNumbers);
}
Edit based on suggestion by juanchopanza:
In C++11 manner, the last variant would look like this
#include <algorithm>
#include <random>
#include <vector>
const int numWantedNumbers = 5;
int main()
{
std::random_device device;
std::mt19937 generator(device());
std::uniform_int_distribution<> distribution(0, 100);
// Generate a vector with all possible numbers and shuffle it.
std::vector<int> result;
for (int i = 0; i <= 100; ++i)
result.push_back(i);
std::random_shuffle(result.begin(), result.end());
// Truncate to the requested size.
result.resize(numWantedNumbers);
}
g++-4.6 compiles it happily, if you add the -std=c++0x switch.
Edit: Make use of std::random_shuffle() (tanks to James Kanze).
Yes there is a rand() function in C++ which can be used including cstdlib header file in your program.
You can implement your program using the following code.
#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;
int main()
{
int max {100};
int min {0};
int number;
int count = 5; //assuming that only 10 numbers we need to print
srand(time(0));
for(int i=1;i<=count;i++)
{
number = rand() % (max - min +1) + min;``
cout<<number<<endl;
}
}