This question already has answers here:
How to generate different random numbers in a loop in C++?
(13 answers)
Closed 7 years ago.
I'm having trouble with a random generator.
I'm trying to print out random values and I'm getting almost the same value every single time.
This is what I have:
void Deck::shuffle() {
StackNode<Card>* top = stack->top;
for (int i = 0; i < stack->numNodes - 1; i++) {
int x = random(i);
StackNode<Card>* temp = findCard(x);
//cout << "Random index was: " << random(i) << endl;
//cout << "Face value of random was: " << temp->data.getFaceVal() << endl;
cout << "Top: " << top->data.getFaceVal() << endl;
cout << "Temp: " << temp->data.getFaceVal() << endl;
swapX(top,temp);
}
}
Here's my random generator function:
int random(int index) {
int r;
srand(time(NULL));
cout << "Index: " << index << endl;
r = rand() % 50;
cout << "Random value: " << r << endl;
return r;
}
I think you can use std::shuffle here for your problem. Like this:
#include <vector>
#include <algorithm>
void Deck::shuffle() {
StackNode<Card>* top = stack->top;
std::vector<StackNode<Card>*> cards;
for (int i = 0; i < stack->numNodes - 1; i++) {
cards.push_back(findCard(i))
}
std::shuffle(cards.begin(), cards.end());
for (auto card : cards) {
std::cout << card->data.getFaceVal() << std::endl;
}
}
By the way, I would recommend you to call srand only once in your code.
rand() is a pseudo random number generator. The numbers it generates appear to be random, but they are generated by a completely deterministic function. The seed that you give it with sand() determines the starting point for the function. If you give it the same seed it will generate the same sequence of random numbers. You can try this and see for your self by seeding with a literal, like srand(200) and running the program several times, you will get the exact same results.
If you want different results each time you have to seed with something that will be different each time the program runs, so time is often used as a seed. In your case you are in a very tight loop so many of the calls in a row use the same time value.
If you call srand() once, before your loop this problem will go away.
Related
Doing an exercise to find the mean and mode of a list of numbers input by a user. I have written the program and it works, but I'm wondering if my function 'calcMode' is too large for this program. I've just started looking into functions which is a first attempt. Would it be better to write smaller functions? and if so what parts can I split? Im pretty new to C++ and also looking if I can improve this code. Is there any changes I can make to make this run more efficient?
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int calcMean(vector<int> numberList)
{
int originNumber = numberList[0];
int nextNumber;
int count = 0;
int highestCount = 0;
int mean = 0;
for (unsigned int i = 0; i <= numberList.size() - 1; i++)
{
nextNumber = numberList[i];
if (nextNumber == originNumber)
count++;
else
{
cout << "The Number " << originNumber << " appears " << count << " times." << endl;
count = 1;
originNumber = nextNumber;
}
}
if (count > highestCount)
{
highestCount = count;
mean = originNumber;
}
cout << "The Number " << originNumber << " appears " << count << " times." << endl;
return mean;
}
int main()
{
vector<int> v;
int userNumber;
cout << "Please type a list of numbers so we can arrange them and find the mean: "<<endl;
while (cin >> userNumber) v.push_back(userNumber);
sort(v.begin(), v.end());
for (int x : v) cout << x << " | ";
cout << endl;
cout<<calcMean(v)<<" is the mean"<<endl;
return 0;
}
One thing to watch out for is copying vectors when you don't need to.
The function signature
int calcMode(vector<int> numberList)
means the numberList will get copied.
int calcMode(const & vector<int> numberList)
will avoid the copy. Scott Meyer's Effective C++ talks about this.
As an aside, calling is a numberList is misleading - it isn't a list.
There are a couple of points that are worth being aware of in the for loop:
for (unsigned int i = 0; i <= numberList.size()-1; i++)
First, this might calculate the size() every time. An optimiser might get rid of this for you, but some people will write
for (unsigned int i = 0, size=numberList.size(); i <= size-1; i++)
The size is found once this way, instead of potentially each time.
They might even change the i++ to ++i. There used to a potential overhead here, since the post-increment might involve an extra temporary value
One question - are you *sure this gives the right answer?
The comparison nextNumber == originNumber is looking at the first number to begin with.
Try it with 1, 2, 2.
One final point. If this is general purpose, what happens if the list is empty?
Would it be better to write smaller functions?
Yes, you can make do the same job using std::map<>; which could be
a much appropriate way to count the repetition of the array elements.
Secondly, it would be much safer to know, what is the size of the
array. Therefore I suggest the following:
std::cout << "Enter the size of the array: " << std::endl;
std::cin >> arraySize;
In the calcMode(), you can easily const reference, so that array
will not be copied to the function.
Here is the updated code with above mentioned manner which you can refer:
#include <iostream>
#include <algorithm>
#include <map>
int calcMode(const std::map<int,int>& Map)
{
int currentRepetition = 0;
int mode = 0;
for(const auto& number: Map)
{
std::cout << "The Number " << number.first << " appears " << number.second << " times." << std::endl;
if(currentRepetition < number.second )
{
mode = number.first; // the number
currentRepetition = number.second; // the repetition of the that number
}
}
return mode;
}
int main()
{
int arraySize;
int userNumber;
std::map<int,int> Map;
std::cout << "Enter the size of the array: " << std::endl;
std::cin >> arraySize;
std::cout << "Please type a list of numbers so we can arrange them and find the mean: " << std::endl;
while (arraySize--)
{
std::cin >> userNumber;
Map[userNumber]++;
}
std::cout << calcMode(Map)<<" is the mode" << std::endl;
return 0;
}
Update: After posting this answer, I have found that you have edited your function with mean instead of mode. I really didn't get it.
Regarding mean & mode: I recommend you to read more. Because in general, a data set can have multiple modes and only one mean.
I personally wouldn't split this code up in smaller blocks, only if i'd want to reuse some code in other methods. But just for this method it's more readable like this.
The order of excecution is aroun O(n) for calc which is quite oke if you ask me
EDITED for full code
I am trying to make a text based rpg game because im really bored and want to put my c++ "skills" at test xd.
But i am having a problem with the functions srand and rand, the function to generate random numbers.
What i want do achieve, is to let the RNG decide the next action of the game. I.e :
#include <iostream>
#include <windows.h>
#include <string>
#include "conio.h"
#include <time.h>
using namespace std;
void GetRandom();
int main()
{
int x;
string name;
srand(time(NULL));
cout << "welcome to adventurers world!" << endl;
cout << "you wake up on an island far far away and you don't know where you are" << endl;
Sleep(2000);
cout << "Please enter the name of your adventurer" << endl;
getline(cin, name);
cout << "hello " << name << endl;
Sleep(1000);
cout << "where would you like to go, " << name << " ?" << endl;
Sleep(1000);
cout << "1. waddle around the beach\n2. go to the cave straight ahead\n3. go into the forest" << endl;
cin >> x;
if(x==1)
{
cout << "you waddle abit around on the beach, and you suddenly " << random;
}
_getch();
}
void random()
{
srand(time(NULL));
int randnumber = rand() % 2 + 1;
randnumber = randnumber;
if(randnumber == 1)
{
cout << "you encounter a mudcrab" << endl;
}
else if (randnumber == 2)
{
cout << "you find a stick" << endl;
}
}
What i want do achieve here is, if the random number generated is 1 do (randnumber == 1) and if it is 2, do (randnumber == 2)
but instead it just gives me a hexidecimal as output.
is my code properly written? am i using the right expression for srand, calculation w/e.
And is this even possible to do? or do i have to write it out manually what will happen next, which wont make it as much a dynamic game.
thanks for your help and time
At the moment, you're not calling the function random, you're displaying its address. Try this:
if(x==1)
{
cout << "you waddle abit around on the beach, and you suddenly ";
random();
}
Don't seed the random generator with the time each time you need a random number. Unless the usage is a long time between (more than a second), that will set the seed to the same value.
Don't name your function random(). That will make the random() function inaccessible. It probably should be choose_random_object() or something like that.
Seed the random number generator once when the program begins, and only reseed it if you need to repeat the random numbers (unlikely in a situation such as this).
Calling a function should return a value which is useful—which yours is not. Call a procedure (a function which does not return a value) for its side-effects, such as printing out a word.
Here's what your code should look like. The comments give an explanation of the changes.
srand(time(NULL)); // srand() needs only to be called once in the beginning.
if(x == 1)
{
cout << "you waddle abit around on the beach, and you suddenly ";
GetRandom(); // call the function to output what you need.
}
void GetRandom() // change the name of the function.
{
int randnumber = rand() % 2 + 1;
// no need for: randnumber = randnumber;
if(randnumber == 1)
{
cout << "you encounter a mudcrab" << endl;
}
else // no need for else if since the random # cannot be anything else but 2
{
cout << "you find a stick" << endl;
}
}
I'm writing a program (actually a game) about fighting with opponents on the arena. Getting down to business, I created the following header, which contains information about fighters:
#ifndef COMPETITOR_H_INCLUDED
#define COMPETITOR_H_INCLUDED
#include <cstdlib>
int ID = 0;
struct competitor
{
std::string name;
int health;
int attack;
};
competitor player;
player.health = 25;
player.attack = (rand()%6)+1;
competitor opponent[2];
opponent[0] = {"Rat", 6, (rand()%4)+1};
opponent[1] = {"Drunkard", 10, (rand()%6)+1};
#endif // COMPETITOR_H_INCLUDED
And here I have my function I have problem with:
int fight()
{
int number = 1;
cout << "Your opponent is " << opponent[ID].name;
cout << endl << "Your opponent's health: " << opponent[ID].health;
cout << endl << "Your health: " << player.health << endl;
while (opponent[ID].health > 0 || player.health > 0)
{
cout << endl << endl << "Round " << number << endl;
cout << opponent[ID].name << " inflicts" << opponent[ID].attack << " damage, ";
cout << "you have " << (player.health = player.health - opponent[ID].attack) << " health points" << endl;
if (player.health <= 0) break;
cout << player.name << " inflicts " << player.attack << " damage, ";
cout << "your opponent has " << (opponent[ID].health = opponent[ID].health - player.attack) << " health points" << endl;
if (opponent[ID].health <= 0) break;
number++;
}
if (player.health > opponent[ID].health)
{
cout << endl << "Congratulations! You managed to defeat your opponent. Prepare for the next fight.";
ID++;
}
else
{
cout << endl << "Unfortunately, you have been defeated. Start again.";
ID = 0;
}
getch();
}
I also have srand(time(NULL)); at the beginning of my main() function. Basically it works, on each program run attack values are different, but they are the same in every round. I have no idea, how to make them to be generated on every while loop.
Every help is greatly appreciated.
Regards
First of all, please do note that it is recommended NOT to use rand. Instead you should use the <random> header and its goodies. If you do not have C++11, there is a nearly one-to-one mapping with Boost.Random.
Then, to generate a random value:
Find yourself a seed (use of std::random_device is advised)
Create a pseudo-randomness engine (std::default_random_engine) and initialize it with the seed
Use an appropriate distribution (in your case, std::uniform_distribution_int<int>) and initialize its parameters (1, 6) for your player, for example.
Finally, each time you need a random number, ask your distribution to provide it by extracting randomness from the engine.
In code:
// Seed with a real random value, if available
std::random_device rd;
// Choose a random mean between 1 and 6
std::default_random_engine e1(rd());
std::uniform_int_distribution<int> uniform_dist(1, 6);
Then each call to uniform_dist(e1) will return a number between 1 and 6, with little to no bias. The important things are that the engine and distribution are long-lived => for a given random sequence you should always draw from the same engine and distribution. It is also perfectly fine to use the same engine with various distributions (there would be a single object otherwise).
Thus, adapted to your code:
using AttackDice = std::uniform_distribution_int<int>;
struct Competitor {
std::string name;
int health;
AttackDice attackDice;
};
Competitor player = {"", 25, AttackDice{1, 6}};
opponent[0] = {"Rat", 6, AttackDice{1, 4}};
opponent[1] = {"Drunkard", 10, AttackDice{1, 6}};
And then in your main:
std::random_device rd;
std::default_random_engine myRandomEngine(rd());
// .. stuff
while (opponent[ID].health > 0 || player.health > 0)
{
cout << endl << endl << "Round " << number << endl;
int playerAttack = player.attackDice(myRandomEngine);
int opponentAttack = opponent[ID].attackDice(myRandomEngine);
// .. resolve
}
// .. stuff
Note: you should only cast the dice one per iteration of the loop, most probably ;)
You ask how to assign a new attack value inside each iteration in the while loop, well just do it:
while (opponent[ID].health > 0 || player.health > 0)
{
opponent[ID].attack = (rand()%6)+1;
player.attack = (rand()%6)+1;
...
Besides setting the attack when you initialize the player instance, you will need to reset it in the while loop.
Basically add this:
while (opponent[ID].health > 0 || player.health > 0)
{
player.attack = (rand()%6)+1; // <<< add this
cout << endl << endl << "Round " << number << endl;
// rest of code is unchanged
}
Since you tagged your question with C++11, in C++11 please don't use rand, it's not a good random function. Use the newly added instead.
Here is some snippet from my code base:
#include <random>
using namespace std;
auto engine = mt19937{ random_device()() };
function<int(int)> rand = [=](int range) mutable {
auto idist = uniform_int_distribution<int>(0, range);
return idist(engine);
};
auto myRandomNumberIn100 = rand(100);
Hint: Store the rand object of type function<int(int> and pass it by reference. If you copy it, rand will always produce the same result.
You're using the old rand function. That's not the best design. C++ nowadays has a better concept: separate the random generator and the random distribution.
In your case, .attack really should be a std::uniform_int_distribution<>. Initialize it with min/max values {1, N} where N varies between the different opponents.
Set up a single random generator: std::mt19937 random; and get a random damage value like this: int damage = opponent[ID].attack(random);
This question already has answers here:
Why does rand() yield the same sequence of numbers on every run?
(7 answers)
Closed 5 years ago.
I just finished coding a Minesweeper type game, and everything's good except for that each time I run the application, it generates the same number (I ran it 3 different times, saved the output to 3 text files and used the diff command in Linux, it didn't find any differences). It's seeded by time(NULL) so it should change every time, right?
Here's my code:
main.cpp
#include <iostream>
#include <cstdlib>
#include <time.h>
#include <string>
#include "Minesweeper/box.h"
#include <cstdio>
int main(int argc, char** argv){
using namespace std;
bool gameOver = false;
int x, y, score = 0;
const int HEIGHT = 10;
const int WIDTH = 10;
unsigned int Time = time(0);
cout << "Welcome to Minesweeper. " << endl;
//setup grid
Box grid[10][10];
for(int i = 0; i < WIDTH; i++)
for(int n = 0; n < HEIGHT; n++){
unsigned int value = rand() %100 + 1;
cout << value << endl;
if(value <= 38){
grid[i][n].setFill(MINE);
//cout << i << "," << n << " is mined." << endl;
}
else
grid[i][n].setFill(EMPTY);
}
for(int r = 0; r < WIDTH; r++)
for(int l = 0; l < HEIGHT; l++)
if(grid[r][l].getFill() == EMPTY)
cout << r << "," << l << " - EMPTY." << endl;
else if (grid[r][l].getFill() == MINE)
cout << r << "," << l << " - MINE." << endl;
while(!gameOver){
cout << "Enter coordinates (x,y): ";
scanf("%i,%i",&x,&y);
if(grid[x][y].getFill() == MINE)
gameOver = true;
else{
cout << "Good job! (You chose " << x << "," << y << ")" << endl;
score++;
}
}
cout << "You hit a mine! Game over!" << endl;
cout << "Final score: " << score << endl;
getchar();
return EXIT_SUCCESS;
}
It's seeded by time(NULL)
If it is, I can't see it. In fact, a search for it in your code returns nothing. The default behaviour, if you don't explicitly seed, is the same as if you had seeded it with the value 1.
You need to explicitly state something like:
srand (time (NULL));
at the start of main somewhere (and make sure you do this once and once only).
Though keep in mind this makes it dependent on the current time - if you start multiple jobs in the same second (or whatever your time resolution is), they'll start with the same seed.
From the C standard (on which C++ is based for these compatibility features):
The srand function uses the argument as a seed for a new sequence of pseudo-random numbers to be returned by subsequent calls to rand. If srand is then called with the same seed value, the sequence of pseudo-random numbers shall be repeated. If rand is called before any calls to srand have been made, the same sequence shall be generated as when srand is first called with a seed value of 1.
You need to seed randomizer. Call srand() at the beginning.
To add to the answers by others, you can use the Mersenne Twister Algorithm, which is a part of the C++11 library. Its fast becoming a standard in many common softwares to generate random numbers.
For example, this is the function I wrote, which I use often to generate random numbers in my other codes:
std::vector<double> mersennetwister(const int& My,const int& Mz,
const int& Ny,const int& Nz)
{
int ysize = (My + 2*Ny + 1);
int zsize = (Mz + 2*Nz + 1);
int matsize = ysize*zsize;
unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
// Seeding the generator with the system time
std::mt19937_64 generator (seed);
// Calling the Mersenne-Twister Generator in C++11
std::uniform_real_distribution<double> distribution(0,1);
// Specifying the type of distribution you want
std::vector<double> randarray(matsize,0);
// Saving random numbers to an array
for (int i=0;i<matsize;++i)
{
randarray[i] = distribution(generator); // Generates random numbers fitting the
// Distribution specified earlier
}
return(randarray);
}
Bottomline: C++11 has some excellent features for numerical operations and it would be a good idea to look into them. As for the Mersenne Twister, http://en.wikipedia.org/wiki/Mersenne_twister
int main()
{
int numbers[30];
int i;
// making array of 30 random numbers under 100
for(i=0;i<30;i++)
{
numbers[i] = rand() % 100;
}
// finding the greatest number in the array
int greatest = 0;
srand(1);
for(i=0;i<30;i++)
{
if ( numbers[i] > greatest)
greatest = numbers[i];
}
How do I then tell the program to display the max value of the array??
Thank you
To display it in the basic console output:
#include <iostream>
...
std::cout << "Max value is: " << greatest << "\n";
#include <iostream>
std::cout << greatest << '\n';
On a sidenote, you might want to call srand() before your call rand() (and might want to supply a more meaningful parameter).
If you are not doing this for home work I would suggest using std::max_element (available in <algorithm>).
std::cout << "Max Value: " << *(std::max_element(number, numbers+30)) << std::endl;
Otherwise, in your program all thats left to do is to print the value. You could use std::cout (available in <iostream>). After you've computed the great in the for loop.
// Dump the value greatest to standard output
std::cout << "Max value: " << greatest << std::endl;
Is this what you're referring to?
printf("%d", greatest);
Make sure to include "cstdio".
std::cout << greatest <<endl;