HiveBrain v1.2.0
Get Started
← Back to all entries
snippetcppMinor

Generate strings of random characters

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
randomcharactersgeneratestrings

Problem

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, i.e. have same seed so that strings don't repeat as long as possible.

#include 
#include 
#include 

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?

Ideone

Solution

Wherever you place static will change the behaviour of your program tremendously. In the end, we have \$2^3 = 8\$ combinations, so let's check some of them.

A random_device may fall back to a pseudorandom engine if no hardware-device for random numbers is available. If you don't use static on random_device in this case, all your strings may end up the same, as the pseudorandom engine may get initialized with a implementation defined fixed seed, e.g.

std::random_device a;
 std::random_device b;
 assert(a() == b());  // worst case if no hardware-device or a low-effort implementation is used


In this case, a mt19937 would get seeded with the same value all the time, and we end up with the same string in every call. This changes if either mt19937 or random_device is static, and may change if dist is static, as a uniform_int_distribution can use several calls to the generator for its internal state.

Of course, if mt19937 is static, you may as well change random_device to a static variant, as it will get evaluated only once. This is the closest your get to a single srand() call at the start of your program. Your uniform_int_distribution still loses its internal state, though.

In the end, it depends on whether we want two generateRandomChar calls to correlate to each other. If they shall not correlate at all, we need to reseed our PRNG with a new non-deterministic number and cannot use static at all.

Or, to put it in another way: if we use static for all three values, then

generateRandomChar(10) + generateRandomChar(10)


is the same as
generateRandomChar(20)
If we use static on mt19937, then both may be the same, depending on both _len and dist.

Either way, if you want to have more control over the generation, you can just provide a generator in your function:

template 
std::string generateRandomChar(Generator & gen, const unsigned int _len)
    {
        ...
    }


Same holds for the distribution.

Other than that, I'd use sizeof instead of 61 to make sure that I don't accidentally change the pool of possible characters without changing the number bounds in dist's constructor:

static constexpr char alphanum[] = "0123456789"
                                   "abcdefghijklmnopqrstuvwxyz"
                                   "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> dis(0, sizeof(alphanum) - 1);

Code Snippets

std::random_device a;
 std::random_device b;
 assert(a() == b());  // worst case if no hardware-device or a low-effort implementation is used
generateRandomChar(10) + generateRandomChar(10)
template <typename Generator>
std::string generateRandomChar(Generator & gen, const unsigned int _len)
    {
        ...
    }
static constexpr char alphanum[] = "0123456789"
                                   "abcdefghijklmnopqrstuvwxyz"
                                   "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> dis(0, sizeof(alphanum) - 1);

Context

StackExchange Code Review Q#141989, answer score: 2

Revisions (0)

No revisions yet.