可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I'm trying to make a game with dice, and I need to have random numbers in it (to simulate the sides of the die. I know how to make it between 1 and 6). Using
#include <cstdlib>
#include <ctime>
#include <iostream>
using namespace std;
int main()
{
srand((unsigned)time(0));
int i;
i = (rand()%6)+1;
cout << i << "\n";
}
doesn't work very well, because when I run the program a few times, here's the output I get:
6
1
1
1
1
1
2
2
2
2
5
2
So I want a command that will generate a different random number each time, not the same one 5 times in a row. Is there a command that will do this?
回答1:
The most fundamental problem of your test application is that you call srand
once and then call rand
one time and exit.
The whole point of srand
function is to initialize the sequence of pseudo-random numbers with a random seed. It means that if you pass the same value to srand
in two different applications (with the same srand
/rand
implementation) you will get exactly the same sequence of rand()
values read after that. But your pseudo-random sequence consists of one element only - your output consists of the first elements of different pseudo-random sequences seeded with time of 1 second precision. So what do you expect to see? When you happen to run application on the same second your result is the same of course (as Martin York already mentioned in a comment to the answer).
Actually you should call srand(seed)
one time and then call rand()
many times and analyze that sequence - it should look random.
回答2:
Using modulo may introduce bias into the random numbers, depending on the random number generator. See this question for more info. Of course, it's perfectly possible to get repeating numbers in a random sequence.
Try some C++11 features for better distribution:
#include <random>
#include <iostream>
int main()
{
std::random_device dev;
std::mt19937 rng(dev());
std::uniform_int_distribution<std::mt19937::result_type> dist6(1,6); // distribution in range [1, 6]
std::cout << dist6(rng) << std::endl;
}
See this question/answer for more info on C++11 random numbers. The above isn't the only way to do this, but is one way.
回答3:
If you are using boost libs you can obtain a random generator in this way:
#include <iostream>
#include <string>
// Used in randomization
#include <ctime>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_int_distribution.hpp>
#include <boost/random/variate_generator.hpp>
using namespace std;
using namespace boost;
int current_time_nanoseconds(){
struct timespec tm;
clock_gettime(CLOCK_REALTIME, &tm);
return tm.tv_nsec;
}
int main (int argc, char* argv[]) {
unsigned int dice_rolls = 12;
random::mt19937 rng(current_time_nanoseconds());
random::uniform_int_distribution<> six(1,6);
for(unsigned int i=0; i<dice_rolls; i++){
cout << six(rng) << endl;
}
}
Where the function current_time_nanoseconds()
gives the current time in nanoseconds which is used as a seed.
Here is a more general class to get random integers and dates in a range:
#include <iostream>
#include <ctime>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_int_distribution.hpp>
#include <boost/random/variate_generator.hpp>
#include "boost/date_time/posix_time/posix_time.hpp"
#include "boost/date_time/gregorian/gregorian.hpp"
using namespace std;
using namespace boost;
using namespace boost::posix_time;
using namespace boost::gregorian;
class Randomizer {
private:
static const bool debug_mode = false;
random::mt19937 rng_;
// The private constructor so that the user can not directly instantiate
Randomizer() {
if(debug_mode==true){
this->rng_ = random::mt19937();
}else{
this->rng_ = random::mt19937(current_time_nanoseconds());
}
};
int current_time_nanoseconds(){
struct timespec tm;
clock_gettime(CLOCK_REALTIME, &tm);
return tm.tv_nsec;
}
// C++ 03
// ========
// Dont forget to declare these two. You want to make sure they
// are unacceptable otherwise you may accidentally get copies of
// your singleton appearing.
Randomizer(Randomizer const&); // Don't Implement
void operator=(Randomizer const&); // Don't implement
public:
static Randomizer& get_instance(){
// The only instance of the class is created at the first call get_instance ()
// and will be destroyed only when the program exits
static Randomizer instance;
return instance;
}
bool method() { return true; };
int rand(unsigned int floor, unsigned int ceil){
random::uniform_int_distribution<> rand_ = random::uniform_int_distribution<> (floor,ceil);
return (rand_(rng_));
}
// Is not considering the millisecons
time_duration rand_time_duration(){
boost::posix_time::time_duration floor(0, 0, 0, 0);
boost::posix_time::time_duration ceil(23, 59, 59, 0);
unsigned int rand_seconds = rand(floor.total_seconds(), ceil.total_seconds());
return seconds(rand_seconds);
}
date rand_date_from_epoch_to_now(){
date now = second_clock::local_time().date();
return rand_date_from_epoch_to_ceil(now);
}
date rand_date_from_epoch_to_ceil(date ceil_date){
date epoch = ptime(date(1970,1,1)).date();
return rand_date_in_interval(epoch, ceil_date);
}
date rand_date_in_interval(date floor_date, date ceil_date){
return rand_ptime_in_interval(ptime(floor_date), ptime(ceil_date)).date();
}
ptime rand_ptime_from_epoch_to_now(){
ptime now = second_clock::local_time();
return rand_ptime_from_epoch_to_ceil(now);
}
ptime rand_ptime_from_epoch_to_ceil(ptime ceil_date){
ptime epoch = ptime(date(1970,1,1));
return rand_ptime_in_interval(epoch, ceil_date);
}
ptime rand_ptime_in_interval(ptime floor_date, ptime ceil_date){
time_duration const diff = ceil_date - floor_date;
long long gap_seconds = diff.total_seconds();
long long step_seconds = Randomizer::get_instance().rand(0, gap_seconds);
return floor_date + seconds(step_seconds);
}
};
回答4:
#include <iostream>
#include <cstdlib>
#include <ctime>
int main() {
srand(time(NULL));
int random_number = std::rand(); // rand() return a number between 0 and RAND_MAX
std::cout << random_number;
return 0;
}
http://en.cppreference.com/w/cpp/numeric/random/rand
回答5:
Can get full Randomer
class code for generating random numbers from here!
If you need random numbers in different parts of the project you can create a separate class Randomer
to incapsulate all the random
stuff inside it.
Something like that:
class Randomer {
// random seed by default
std::mt19937 gen_;
std::uniform_int_distribution<size_t> dist_;
public:
/* ... some convenient ctors ... */
Randomer(size_t min, size_t max, unsigned int seed = std::random_device{}())
: gen_{seed}, dist_{min, max} {
}
// if you want predictable numbers
void SetSeed(unsigned int seed) {
gen_.seed(seed);
}
size_t operator()() {
return dist_(gen_);
}
};
Such a class would be handy later on:
int main() {
Randomer randomer{0, 10};
std::cout << randomer() << "\n";
}
You can check this link as an example how i use such Randomer
class to generate random strings. You can also use Randomer
if you wish.
回答6:
Here is a solution. Create a function that returns the random number and place it
outside the main function to make it global. Hope this helps
#include <iostream>
#include <cstdlib>
#include <ctime>
int rollDie();
using std::cout;
int main (){
srand((unsigned)time(0));
int die1;
int die2;
for (int n=10; n>0; n--){
die1 = rollDie();
die2 = rollDie();
cout << die1 << " + " << die2 << " = " << die1 + die2 << "\n";
}
system("pause");
return 0;
}
int rollDie(){
return (rand()%6)+1;
}
回答7:
for random every RUN file
size_t randomGenerator(size_t min, size_t max) {
std::mt19937 rng;
rng.seed(std::random_device()());
//rng.seed(std::chrono::high_resolution_clock::now().time_since_epoch().count());
std::uniform_int_distribution<std::mt19937::result_type> dist(min, max);
return dist(rng);
}
回答8:
Generate a different random number each time, not the same one six times in a row.
Use case scenario
I likened Predictability's problem to a bag of six bits of paper, each with a value from 0 to 5 written on it. A piece of paper is drawn from the bag each time a new value is required. If the bag is empty, then the numbers are put back into the bag.
...from this, I can create an algorithm of sorts.
Algorithm
A bag is usually a Collection
. I chose a bool[]
(otherwise known as a boolean array, bit plane or bit map) to take the role of the bag.
The reason I chose a bool[]
is because the index of each item is already the value of each piece of paper. If the papers required anything else written on them then I would have used a Dictionary<string, bool>
in its place. The boolean value is used to keep track of whether the number has been drawn yet or not.
A counter called RemainingNumberCount
is initialised to 5
that counts down as a random number is chosen. This saves us from having to count how many pieces of paper are left each time we wish to draw a new number.
To select the next random value I'm using a for..loop
to scan through the bag of indexes, and a counter to count off when an index
is false
called NumberOfMoves
.
NumberOfMoves
is used to choose the next available number. NumberOfMoves
is first set to be a random value between 0
and 5
, because there are 0..5 available steps we can make through the bag. On the next iteration NumberOfMoves
is set to be a random value between 0
and 4
, because there are now 0..4 steps we can make through the bag. As the numbers are used, the available numbers reduce so we instead use rand() % (RemainingNumberCount + 1)
to calculate the next value for NumberOfMoves
.
When the NumberOfMoves
counter reaches zero, the for..loop
should as follows:
- Set the current Value to be the same as
for..loop
's index.
- Set all the numbers in the bag to
false
.
- Break from the
for..loop
.
Code
The code for the above solution is as follows:
(put the following three blocks into the main .cpp file one after the other)
#include "stdafx.h"
#include <ctime>
#include <iostream>
#include <string>
class RandomBag {
public:
int Value = -1;
RandomBag() {
ResetBag();
}
void NextValue() {
int BagOfNumbersLength = sizeof(BagOfNumbers) / sizeof(*BagOfNumbers);
int NumberOfMoves = rand() % (RemainingNumberCount + 1);
for (int i = 0; i < BagOfNumbersLength; i++)
if (BagOfNumbers[i] == 0) {
NumberOfMoves--;
if (NumberOfMoves == -1)
{
Value = i;
BagOfNumbers[i] = 1;
break;
}
}
if (RemainingNumberCount == 0) {
RemainingNumberCount = 5;
ResetBag();
}
else
RemainingNumberCount--;
}
std::string ToString() {
return std::to_string(Value);
}
private:
bool BagOfNumbers[6];
int RemainingNumberCount;
int NumberOfMoves;
void ResetBag() {
RemainingNumberCount = 5;
NumberOfMoves = rand() % 6;
int BagOfNumbersLength = sizeof(BagOfNumbers) / sizeof(*BagOfNumbers);
for (int i = 0; i < BagOfNumbersLength; i++)
BagOfNumbers[i] = 0;
}
};
A Console class
I create this Console class because it makes it easy to redirect output.
Below in the code...
Console::WriteLine("The next value is " + randomBag.ToString());
...can be replaced by...
std::cout << "The next value is " + randomBag.ToString() << std::endl;
...and then this Console
class can be deleted if desired.
class Console {
public:
static void WriteLine(std::string s) {
std::cout << s << std::endl;
}
};
Main method
Example usage as follows:
int main() {
srand((unsigned)time(0)); // Initialise random seed based on current time
RandomBag randomBag;
Console::WriteLine("First set of six...\n");
randomBag.NextValue();
Console::WriteLine("The next value is " + randomBag.ToString());
randomBag.NextValue();
Console::WriteLine("The next value is " + randomBag.ToString());
randomBag.NextValue();
Console::WriteLine("The next value is " + randomBag.ToString());
randomBag.NextValue();
Console::WriteLine("The next value is " + randomBag.ToString());
randomBag.NextValue();
Console::WriteLine("The next value is " + randomBag.ToString());
randomBag.NextValue();
Console::WriteLine("The next value is " + randomBag.ToString());
Console::WriteLine("\nSecond set of six...\n");
randomBag.NextValue();
Console::WriteLine("The next value is " + randomBag.ToString());
randomBag.NextValue();
Console::WriteLine("The next value is " + randomBag.ToString());
randomBag.NextValue();
Console::WriteLine("The next value is " + randomBag.ToString());
randomBag.NextValue();
Console::WriteLine("The next value is " + randomBag.ToString());
randomBag.NextValue();
Console::WriteLine("The next value is " + randomBag.ToString());
randomBag.NextValue();
Console::WriteLine("The next value is " + randomBag.ToString());
Console::WriteLine("\nThird set of six...\n");
randomBag.NextValue();
Console::WriteLine("The next value is " + randomBag.ToString());
randomBag.NextValue();
Console::WriteLine("The next value is " + randomBag.ToString());
randomBag.NextValue();
Console::WriteLine("The next value is " + randomBag.ToString());
randomBag.NextValue();
Console::WriteLine("The next value is " + randomBag.ToString());
randomBag.NextValue();
Console::WriteLine("The next value is " + randomBag.ToString());
randomBag.NextValue();
Console::WriteLine("The next value is " + randomBag.ToString());
Console::WriteLine("\nProcess complete.\n");
system("pause");
}
Example output
When I ran the program, I got the following output:
First set of six...
The next value is 2
The next value is 3
The next value is 4
The next value is 5
The next value is 0
The next value is 1
Second set of six...
The next value is 3
The next value is 4
The next value is 2
The next value is 0
The next value is 1
The next value is 5
Third set of six...
The next value is 4
The next value is 5
The next value is 2
The next value is 0
The next value is 3
The next value is 1
Process complete.
Press any key to continue . . .
Closing statement
This program was written using Visual Studio 2017, and I chose to make it a Visual C++ Windows Console Application
project using .Net 4.6.1
.
I'm not doing anything particularly special here, so the code should work on earlier versions of Visual Studio too.
回答9:
This code produces random numbers from n
to m
.
int random(int from, int to){
return rand() % (to - from + 1) + from;
}
example:
int main(){
srand(time(0));
cout << random(0, 99) << "\n";
}
回答10:
Here is a simple random generator with approx. equal probability of generating positive and negative values around 0:
int getNextRandom(const size_t lim)
{
int nextRand = rand() % lim;
int nextSign = rand() % lim;
if (nextSign < lim / 2)
return -nextRand;
return nextRand;
}
int main()
{
srand(time(NULL));
int r = getNextRandom(100);
cout << r << endl;
return 0;
}