Nick Lewers
Nick Lewers

Reputation: 101

random_shuffle and srand() give me the same result every time

I'm trying to produce do some processing on a random permutation of the alphabet, however each permutation produces the same result despite using srand(myseed)
I have included the <algorithm> header.

string create_permutation(unsigned seed)
{
    srand(seed);
    string permutation = ALPHABET;
    random_shuffle(permutation.begin(), permutation.end());
    return permutation;
}

cout << create_permutation(2) << endl; // or 3, 4, 5 etc
// continuously returns 'XQACKHSLOJ,TRBZNGV.W FIUEYDMP

Any help would be greatly appreciated.

EDIT: Minimal, Complete, and Verifiable example

EDIT 2: adjustment to mcve

#include <iostream>
#include <algorithm>   

using namespace std;

const string ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ.,' ";
string create_permutation(unsigned seed)
{
    srand(seed);
    string permutation = ALPHABET;
    random_shuffle(permutation.begin(), permutation.end());
    return permutation;
}

int main(){    
    cout << create_permutation(2) << endl; // or 3, 4, 5 etc
    // continuously returns 'XQACKHSLOJ,TRBZNGV.W FIUEYDMP
    return 0;
}

Upvotes: 1

Views: 4000

Answers (2)

user4581301
user4581301

Reputation: 33932

  1. When you seed the random number generator, you set the starting point for an algorithm that will provide a sequence of psuedorandom numbers. If you always use the same seed, the algorithm will always have the same starting point and always generate the same sequence of numbers.
  2. Every time you call srand, you reset the seed and reset the starting point for the algorithm.
    1. A common bug here is to repeatedly call srand. Unless you have a very good reason not to (and if you do, you probably shouldn't use rand and srand at all. Look to the <random> library) you should call srand once at the beginning of the program to seed the generator once and ever after use that seed.
    2. srand is also quite expensive. From a performance standpoint, you don't want to call it often.

So: since every call to create_permutation calls srand with the seed parameter and create_permutation is always called with the same seed value, for a given implementation of the random number generator, create_permutation will always use the same random number sequence and thus generate the same permutation.

A quick example that will generate different permutations, so long as the program is not run more often than once per second is

#include <iostream>
#include <string>
#include <algorithm>
#include <ctime>

const std::string ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ.,' ";
std::string create_permutation()
{
    std::string permutation = ALPHABET;
    std::random_shuffle(permutation.begin(), permutation.end());
    return permutation;
}

int main(){
    srand(time(NULL)); // caveat: time's minimum resolution is 1 second.
                       // multiple executions of this program within 1
                       // second will get the same time and use the same seed.
    std::cout << create_permutation() << std::endl;
    std::cout << create_permutation() << std::endl;
    std::cout << create_permutation() << std::endl;
    std::cout << create_permutation() << std::endl;
    std::cout << create_permutation() << std::endl;
    return 0;
}

A more modern approach:

#include <random>
#include <algorithm>
#include <iostream>
#include<string>

const std::string ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ.,' ";
std::random_device rd; // Warning: implementation of this is allowed to be stupid and 
                       // return the same value every time. Does not work in mingw 4.8
                       // Can't speak for 4.9 or greater
std::mt19937 randomizer(rd());


std::string create_permutation()
{
    std::string permutation = ALPHABET;
    std::shuffle(permutation.begin(), permutation.end(), randomizer);
    return permutation;
}


int main()
{
    std::cout << create_permutation() << std::endl;
    std::cout << create_permutation() << std::endl;
    std::cout << create_permutation() << std::endl;
    std::cout << create_permutation() << std::endl;
    std::cout << create_permutation() << std::endl;
}

Upvotes: 0

Matt C
Matt C

Reputation: 4555

The problem

  • The shuffles aren't random because shuffle_random is using the same seed for the random number generator each time it is called.

srand does not seed the random_shuffle function, it seeds rand and random_shuffle usually calls rand, but does not have to.

random_shuffle has two forms:

  • One that takes 2 arguments (begin/end iterators)

  • One that takes 3 (begin/end iterator and a random generator).

You have demonstrated that you know how to use the first form, but the problem with the first form is that it is implemented differently on different platforms and with different compilers. It may not use rand() at all, which is the function that srand seeds.

You should use the 3 argument form and provide the random number generator as a parameter to the function.

You can follow this detailed answer to learn how to make your own random number generator, or you can provide rand() to the random_shuffle function as the random number generator.

Upvotes: 7

Related Questions