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

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)

Related

How to bind numbers together into an integer without addition?

For example:
If I end up with multiple digits and all of those numbers are generated randomly (1-9) up to 7 digits total and I want them all to end up as a whole integer, how do I go by doing this?
Here is something I have tried:
static void generate_key(std::map<std::string, int>& key, std::string c_user)
{
unsigned int hold[7]{}; // will hold all the random integers seperate to add onto final
int *final = new int; // will store a complete 7 digit integer here later on
for (int i = 0; i < 7; i++)
{
unsigned int num = rand() % 9;
hold[i] = num;
}
*final = hold[0] + hold[1] + hold[2] + hold[3]; // I know this sums everything but how do I get it to just add all the numbers into a single integer?!
key.emplace(c_user, final);
std::cout << "Key created: " << *final << '\n';
delete final;
}
Hold all integers seperate from eachother:
unsigned int hold[7]{};
Create random digits:
for (int i = 0; i < 7; i++)
{
unsigned int num = rand() % 9;
hold[i] = num;
}
Store and add all integers into *final integer
int *final = new int;
I want to generate a 7 digit key and each of every single digit is randomized. I then want to add them all up to make a whole integer that gets put into a map where a string has already been created and that key will represent the string. Do you get what I'm saying here? Sorry if my explanation is terrible I'm just too lost here..
Here is the full code if thats helpful:
#include <iostream>
#include <string>
#include <map>
#include <vector>
#include <stdlib.h>
class Account
{
private:
std::string username;
std::string password;
public:
Account(std::string username, std::string password)
{
this->username = username;
this->password = password;
}
~Account() {};
std::string get_username()const { return username; }
std::string get_password()const { return password; }
};
static void create_account(std::string user, std::string pass, std::vector<Account> &acc)
{
Account *account = new Account(user, pass);
acc.push_back(*account);
delete account;
}
static void generate_key(std::map<std::string, int>& key, std::string c_user)
{
unsigned int hold[7]{};
int *final = new int;
for (int i = 0; i < 7; i++)
{
unsigned int num = rand() % 9;
hold[i] = num;
}
*final = hold[0] + hold[1] + hold[2] + hold[3];
key.emplace(c_user, final);
std::cout << "Key created: " << *final << '\n';
delete final;
}
std::vector<Account> accounts;
std::map<std::string, int> keys;
int main()
{
srand(time(NULL));
create_account("random", "triathlon", accounts);
generate_key(keys, accounts[0].get_username());
return 0;
}
static void create_account(std::string, std::string, std::vector<Account> &acc);
static void generate_key(std::map<std::string, int>&, std::string);
If you want to generate a 7 digit number where all of the digits are unique, then you can store the valid digits in a vector and then shuffle that vector and take the first 7 elements as the digit to make the number with. That could be done like:
std::vector<char> digits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}
std::random_device rd;
std::mt19937 g(rd());
std::suffle(digits.being(), digits.end(), g);
std::string number{digits.begin(), digits.begin() + 7}
and now number is a 7 digit string where each digit only appears once. If you want to not have a leading zero, then you would need to check if the first element is a zero and if so, shuffle again or just takes elements 1 through 7.
If you just want to generate a 7 digit number, but you don't care if there are duplicate digits, then you can do that with a std::string and a simple for loop like:
std::random_device rd;
std::mt19937 g(rd());
std::uniform_int_distribution<> dist(0, 9);
std::string number;
for (int i = 0; i < 7; ++i)
{
number.push_back('0' + dist(g))
}
If you just want a random seven digit number with unique digits you can generate that directly using the typical random shuffle algorithm where you generate a random index into an array take that item, swap the back of the array with it, and pop back of the array.
I forget what this algorithm is called.
In your case you only need to do seven iterations starting with the nine digits. Code below:
#include <vector>
#include <array>
#include <algorithm>
#include <iostream>
int remove_rnd_item( std::vector<int>& digits ) {
int n = rand() % digits.size(); // should use a better RNG...
int rnd_digit = digits[n];
//swap with the last digit and pop...
std::swap(digits[n], digits.back());
digits.pop_back();
return rnd_digit;
}
int random_seven_digit_number_with_unique_digits() {
std::vector<int> digits = { 0,1,2,3,4,5,6,7,8,9 };
std::array<int, 7> rnd_digits;
std::generate(rnd_digits.begin(), rnd_digits.end(),
[&digits]() {
return remove_rnd_item(digits);
}
);
// convert the digits vector to a single int, if that is what you want
int tens_place = 1;
int val = 0;
for (auto digit : rnd_digits) {
val += tens_place * digit;
tens_place *= 10;
}
return val;
}
int main() {
for (int i = 0; i < 10; ++i) {
std::cout << random_seven_digit_number_with_unique_digits() << "\n";
}
}
I assume that the requirement to have all unique digits in the customer ID is erroneous.
To get a random 7-digit number you only need:
#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<> distrib(0, 9'999'999);
std::cout << distrib(gen) << std::endl;
}
If you want to avoid leading zeroes, use distrib(1'000'000, 9'999'999);
Weeee! Hi!.I'm a beginner too!. and yeah. I think you can do that like this!.
int boop(){
int ding=1000000;
int a[7]={1,4,6,3,4,2,4};
int re=0;
for(int i=0;i<7;i++){
re+=a[i]*ding;
ding/=10;
}
return re;
}
or just make your own function to make a random number ranging from 0 to 1. and multiply it with 9999999. (kinda recommended).
#include <iostream>
#include <math.h>
float boop(int seed){
return abs(sin( cos(seed*3972)*38472));
}
int main(){
float f=boop(34)*9999999;
std::cout<<(int)f;
return 0;
}

What is the most efficient way to generate random strings in 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.

Inquire about using a random integer as the size of a vector

so I have trouble remembering how to call upon random numbers in C++. I'm not sure how to, so if someone could remind me that would be fantastic. I'm trying to make a vector with a random size filled with random integers. Here is my following code:
#include <cstdlib>
#include <iostream>
#include <vector>
int main()
{
int randSize = rand();
int sum = 0;
int product = 1;
std::vector<int> numbers(randSize);
int output;
for (int i = 0; i < numbers.size(); i++)
{
int randNum = rand();
numbers[i] = randNum;
output = numbers[i]&2;
if (output == 0)
{
sum += numbers[i];
}
else
{
product = product * numbers[i];
}
}
std::cout << "The sum of even numbers is " << sum << "\n";
std::cout << "The product of off numbers is " << product << "\n";
}
As already pointed out in the comments, best forget about rand() and use the facilities provided by the standard library in <random> instead. For example:
std::vector<int> makeRandomBunch(int min, int max, std::size_t min_elements, std::size_t max_elements)
{
std::mt19937 generator(42);
std::uniform_int_distribution<std::size_t> size_distribution(min_elements, max_elements);
std::size_t num_elements = size_distribution(generator);
std::vector<int> data;
data.reserve(num_elements);
std::uniform_int_distribution<int> element_distribution(min, max);
std::generate_n(std::back_inserter(data), num_elements, [&](){ return element_distribution(generator); });
return data;
}
Here, we're using an mt19937 pseudorandom number generator (don't get spooked by the name, it's just named after the algorithm it uses) seeded with the value 42 as our source of randomness. An std::uniform_int_distribution can be used to shape the randomness provided by a random generator into random integers sampled from a given range. We use a uniform_int_distribution to randomly pick a size between elements_min and elements_max for our vector and reserve that amount of space in our vector. We use another uniform_int_distribution which we'll pick our int elements from (ranging between min and max). Finally, we use std::generate_n in combination with a back_inserter to fill our vector with elements and return the vector…
live example here

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.

C++ Problems with arrays, begin and end in functions

Hello I am trying to write a script that picks a random number and then excludes that number afterwards.
#include <iostream>
#include <ctime>
#include <random>
#include <iterator>
using namespace std;
random_device rd; // non-deterministic generator
mt19937 gen(rd()); // to seed mersenne twister.
uniform_int_distribution<> dist(1, 52); // distribute results between 1 and 6 inclusive.
int testFunc(int cardArray, int cardArray2, int k) {
cardArray[k] = dist(gen);
copy(begin(cardArray), end(cardArray), begin(cardArray2));
cardArray2[k] = 0;
bool exists = find(begin(cardArray2), end(cardArray2), cardArray[k]) != end(cardArray2);
cardArray[k] = dist(gen);
cout << i + 1 << ": " << cardArray[k] << " " << exists << endl;
return 0;
}
int main()
{
int cardArray[52] = { 0 };
int cardArray2[52] = { 0 };
int i = 0;
for (int n = 0; cardArray[n] == 0 && n < 52; n++) {
cardArray[i] = dist(gen);
copy(begin(cardArray), end(cardArray), begin(cardArray2));
cardArray2[i] = 0;
bool exists = find(begin(cardArray2), end(cardArray2), cardArray[i]) != end(cardArray2);
cardArray[i] = dist(gen);
cout << i + 1 << ": " << cardArray[i] << " " << exists << endl;
i++;
}
cout << endl;
cin.ignore();
return 0;
}
So there's a few problems so far. Here are the errors:
no instance of overloaded function "end" matches the argument list
no instance of overloaded function "begin" matches the argument list
expression must have pointer - to - object type
I just can't figure out what's wrong. The function itself works fine if it's just in main but I need to be able to call it.
Please tell me if I need to post more information.
You are taking in ints in you function not int*
int testFunc(int cardArray, int cardArray2, int k)
should be
int testFunc(int* cardArray, int* cardArray2, int k)
Unfortunately this will stop std::begin and std::end from working as they need an array and not a pointer. To pass the arrays to function you need to take them by reference. To do that we can use a template like:
template<typename T, std::size_t N, std::size_t M>
int testFunc(T (&cardArray)[N], T (&cardArray2)[M], int k)
Or we can skip using native arrays and use a std::array or std::vector