Reputation: 661
I have some C++ code which is calling another function above it. That function depends on being random for one of its selections, but it is not acting randomly. It is finding the same number every time. How can I guarantee it to be random?
I tried calling srand at the beginning of the code, but I did not help. Maybe I am not understanding something important about srand.
Here is the code, I know it is a lot but the most important stuff is at the very top:
vector<vector<int> > Successor(vector<int> sudoku_state) {
srand (time(NULL));
//Core successor function logic: I choose one of the non-unchangable states at random, and change it to any of its other 3 possible values. Eg. If a cell with a '1' is chosen at random, then I return the sudoku board with a '2' a '3' and a '4' at that location, in a vector of vectors.
vector<vector<int> > list_of_all_successors;
int random_indice;
random_indice = rand()%16;
//make sure it is not one of the unchangable squares
while((std::find(unchangables.begin(), unchangables.end(), random_indice) != unchangables.end()))
{
random_indice = rand()%16;
}
if(sudoku_state[random_indice]==1) {
sudoku_state[random_indice]=2;
list_of_all_successors.push_back(sudoku_state);
sudoku_state[random_indice]=3;
list_of_all_successors.push_back(sudoku_state);
sudoku_state[random_indice]=4;
list_of_all_successors.push_back(sudoku_state);
}
else if(sudoku_state[random_indice]==2) {
sudoku_state[random_indice]=1;
list_of_all_successors.push_back(sudoku_state);
sudoku_state[random_indice]=3;
list_of_all_successors.push_back(sudoku_state);
sudoku_state[random_indice]=4;
list_of_all_successors.push_back(sudoku_state);
}
else if(sudoku_state[random_indice]==3) {
sudoku_state[random_indice]=1;
list_of_all_successors.push_back(sudoku_state);
sudoku_state[random_indice]=2;
list_of_all_successors.push_back(sudoku_state);
sudoku_state[random_indice]=4;
list_of_all_successors.push_back(sudoku_state);
}
else { //then we know == 4
sudoku_state[random_indice]=2;
list_of_all_successors.push_back(sudoku_state);
sudoku_state[random_indice]=3;
list_of_all_successors.push_back(sudoku_state);
sudoku_state[random_indice]=1;
list_of_all_successors.push_back(sudoku_state);
}
return list_of_all_successors;
}
Here is the caller:
//The core hillclimbing functionality. Performs the logic of the hillclimbing algorithm on the given initial sudoku board, called initial_state. initial_state is represented as a vector of int of size 16. The returned value called goal_state, is also a vector of int of size 16.
vector<int> hillClimber(vector<int> initial_state) {
vector<int> goal_state; // the final goal state
vector<int> sudoku_config_with_lowest_flaws = initial_state; //the state being worked with in each iteration
int minimum = 500; //the current minimum amount of flaws found in a state (set to 500 initially as a max)
int iterations = 0; //if iterations reaches a high amount, random restart.
while(true) {
cout << "\nBeginning of hillClimber loop reached.\n";
vector<vector<int> > all_successors = Successor(sudoku_config_with_lowest_flaws);
cout << "Potential Successors to beginning state determined. Now analyzing. \n";
//****Core hillClimber logic:
//Loop over the successors and determine one with lowest amount of flaws.
int j = 0;
while(j<all_successors.size()) {
vector<int> next_state = all_successors[j];
int number_of_flaws = Evaluator(next_state);
if(number_of_flaws <= minimum) {
minimum = number_of_flaws;
sudoku_config_with_lowest_flaws = next_state;
}
if(number_of_flaws==0) {
//print the final solution
cout << "**********A SOLUTION HAS BEEN DETERMINED**********\n";
for(int i=0; i < sudoku_config_with_lowest_flaws.size(); i++){
cout << sudoku_config_with_lowest_flaws[i];
if(i==3 || i==7 || i==11)
cout << '\n';
}
return sudoku_config_with_lowest_flaws;
}
j++;
}
//print the next lowest state configuration found:
cout << "Next configuration (passed into next loop of hillClimber) shown below: (Still has " << minimum << " flaws). \n";
for(int i=0; i < sudoku_config_with_lowest_flaws.size(); i++){
cout << sudoku_config_with_lowest_flaws[i];
if(i==3 || i==7 || i==11)
cout << '\n';
}
iterations++;
if(iterations>50) {
//randomized restart area: re-randomizes the initial_state, and runs again.
cout << "\n ***Reached a local min -- Had to restart hillClimber with a new initial_state! *** \n";
int i = 0;
for(i = 0; i<initial_state.size(); i++) {
if(!(std::find(unchangables.begin(), unchangables.end(), i) != unchangables.end())) {
initial_state[i] = rand() % 4 + 1;
}
}
cout << "New initial state: \n";
for(int i=0; i < initial_state.size(); i++){
cout << initial_state[i];
if(i==3 || i==7 || i==11)
cout << '\n';
}
cout << "\n Press Enter to continue \n";
getchar();
return hillClimber(initial_state);
}
}
}
In the random number generation in the function called Successor, it will always find the same number over and over, which ruins the functionality of it.
Upvotes: 0
Views: 247
Reputation: 476930
Seed the random number generator once in the program, not every time you want to generate random numbers!
int main()
{
std::srand( /* some random source */ );
run_rest_of_program();
}
A source of randomness could be something naive like std::time(nullptr)
(baby's first seed), or more appropriately something like std::random_device{}()
. For the latter, #include <random>
.
You should use C++'s <random>
facilities anyway, rather than the poor C rand()
. For example:
#include <iostream>
#include <random>
int main()
{
std::mt19937 rng(std::random_device{}()); // the PRNG
std::normal_distribution<double> dist; // a distribution
// Print 100 standard-normally distributed numbers.
for (int i = 0; i != 100; ++i)
{
std::cout << dist(rng) << '\n';
}
}
Upvotes: 3