我正在尝试用骰子制作游戏,我需要在其中有随机数字(模拟骰子的两侧。我知道如何在1到6之间进行)。使用
#include <cstdlib>
#include <ctime>
#include <iostream>
using namespace std;
int main()
{
srand((unsigned)time(0));
int i;
i = (rand()%6)+1;
cout << i << "\n";
}
效果不好,因为当我运行程序几次时,这是我得到的输出:
6
1
1
1
1
1
2
2
2
2
5
2
所以我想要一个每次生成不同随机数的命令,而不是连续5次生成随机数。是否有命令可以做到这一点?
答案 0 :(得分:132)
使用modulo可能会在随机数中引入偏差,具体取决于随机数生成器。 See this question for more info.当然,完全有可能以随机顺序重复数字。
尝试一些C ++ 11功能以便更好地分发:
#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.以上不是唯一的方法,但这是一种方式。
答案 1 :(得分:49)
您的测试应用程序最基本的问题是,您只需拨打srand
一次,然后再拨打rand
并退出。
srand
函数的重点是用随机种子初始化伪随机数序列。这意味着如果您在两个不同的应用程序中将相同的值传递给srand
(使用相同的srand
/ rand
实现),您将获得完全相同的rand()
值序列之后阅读。但是你的伪随机序列只包含一个元素 - 你的输出由不同的伪随机序列的第一个元素组成,这些元素以1秒精度的时间播种。那你期望看到什么?当您碰巧在同一秒运行应用程序时,您的结果当然是相同的(正如Martin York在答案评论中已提到的那样)。
实际上,您应该拨打srand(seed)
一次,然后多次拨打rand()
并分析该序列 - 它应该是随机的。
答案 2 :(得分:9)
如果您使用boost库,则可以通过以下方式获取随机生成器:
#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;
}
}
函数current_time_nanoseconds()
给出当前时间(以纳秒为单位),用作种子。
这是一个更通用的类,用于获取范围内的随机整数和日期:
#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);
}
};
答案 3 :(得分:7)
#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;
}
答案 4 :(得分:7)
每当您使用C ++编程语言对random number generation
进行基本的网络搜索时,通常都会首先出现此问题!我想大声疾呼,以期希望更好地阐明C ++中伪随机数生成的概念,以便将来的程序员不可避免地会在网上搜索相同的问题!
伪随机数生成涉及利用deterministic algorithm生成数字序列的过程,该序列的性质近似于随机数。我说近似,因为真正的随机性在数学和计算机科学中相当elusive mystery。因此,为什么使用伪随机一词在学究上更正确!
在实际使用PRNG即pseudo-random number generator
之前,必须为算法提供一个初始值,通常也称为 seed 。但是,只能使用算法本身一次 设置种子!
/// Proper way!
seed( 1234 ) /// Seed set only once...
for( x in range( 0, 10) ):
PRNG( seed ) /// Will work as expected
/// Wrong way!
for( x in rang( 0, 10 ) ):
seed( 1234 ) /// Seed reset for ten iterations!
PRNG( seed ) /// Output will be the same...
因此,如果您想要一个好的数字序列,则必须为PRNG提供充足的种子!
C ++具有的C的向后兼容标准库使用在cstdlib
头文件中找到的称为linear congruential generator的东西!该PRNG通过利用模块化算术的不连续分段函数来起作用,即,喜欢使用modulo operator '%'
的快速算法。关于@Predictability提出的原始问题,以下是此PRNG的常用用法:
#include <iostream>
#include <cstdlib>
#include <ctime>
int main( void )
{
int low_dist = 1;
int high_dist = 6;
std::srand( ( unsigned int )std::time( nullptr ) );
for( int repetition = 0; repetition < 10; ++repetition )
std::cout << low_dist + std::rand() % ( high_dist - low_dist ) << std::endl;
return 0;
}
C的PRNG的常用用法包含很多问题,例如:
std::rand()
的整个界面不是很直观,例如,以@Predictability所需的方式在[1,6]之间生成数字。std::rand()
的常用用法消除了伪随机数均匀分布的可能性。std::rand()
是通过std::srand( ( unsigned int )std::time( nullptr ) )
播种的常见方法是不正确的,因为time_t
被认为是restricted type。因此,不能保证从time_t
到unsigned int
的转换! 有关使用C的PRNG的总体问题以及如何规避它们的更多详细信息,请参阅Using rand() (C/C++): Advice for the C standard library’s rand() function!
自从ISO / IEC 14882:2011标准(即C ++ 11)发布以来,random
库已经脱离C ++编程语言已有一段时间了。该库配备了多个 PRNG,以及不同的发行类型,例如:uniform distribution,normal distribution,binomial distribution等。以下源代码示例演示了random
库的非常基本的用法,涉及@Predictability的原始问题:
#include <iostream>
#include <cctype>
#include <random>
using u32 = uint_least32_t;
using engine = std::mt19937;
int main( void )
{
std::random_device os_seed;
const u32 seed = os_seed();
engine generator( seed );
std::uniform_int_distribution< u32 > distribute( 1, 6 );
for( int repetition = 0; repetition < 10; ++repetition )
std::cout << distribute( generator ) << std::endl;
return 0;
}
在上面的示例中,使用了整数值的均匀分布的32位Mersenne Twister引擎。 (源代码中的引擎名称听起来很奇怪,因为它的名称来自其period的2 ^ 19937-1)。该示例还使用std::random_device
来播种引擎,该引擎从操作系统获取其值(如果您使用的是Linux系统,那么std::random_device
将从/dev/urandom
返回一个值)。>
请注意,您不必使用std::random_device
来播种任何引擎。您可以使用 constants 甚至是chrono
库!您也不必使用std::mt19937
引擎的32位版本,还有其他选项!有关random
库功能的更多信息,请参考cplusplus.com
总而言之,C ++程序员不应该再使用std::rand()
了,不是因为它的坏,而是因为当前的标准提供了更好的替代方法,这些替代方法更容易实现。 >和 reliable 。希望你们中的许多人发现这有帮助,尤其是最近通过网络搜索generating random numbers in c++
的人!
答案 5 :(得分:2)
Can get full Randomer
class code for generating random numbers from here!
如果在项目的不同部分需要随机数,则可以创建一个单独的类Randomer
来封装其中的所有random
内容。
类似的东西:
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_);
}
};
这样的课程以后会很方便:
int main() {
Randomer randomer{0, 10};
std::cout << randomer() << "\n";
}
您可以检查this link as an example how i use这样的Randomer
类以生成随机字符串。您也可以根据需要使用Randomer
。
答案 6 :(得分:2)
我知道如何在不使用任何头文件、编译器内部函数或其他任何东西的情况下,在 C++ 中生成随机数。
#include <cstdio> // Just for printf
int main() {
auto val = new char[0x10000];
auto num = reinterpret_cast<unsigned long long>(val);
delete[] val;
num = num / 0x1000 % 10;
printf("%llu\n", num);
}
运行一段时间后,我得到以下统计数据:
0: 5268
1: 5284
2: 5279
3: 5242
4: 5191
5: 5135
6: 5183
7: 5236
8: 5372
9: 5343
看起来很随意。
工作原理:
答案 7 :(得分:1)
这是一个解决方案。创建一个返回随机数并放置它的函数 在主要功能之外使其全球化。希望这会有所帮助
#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;
}
答案 8 :(得分:1)
随机每个RUN文件
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);
}
答案 9 :(得分:1)
每次生成一个不同的随机数,而不是连续六次生成相同的随机数。
用例场景
我把可预测性的问题比作一包六张纸,每张纸上写着0到5的值。每次需要新值时,从袋子中抽出一张纸。如果袋子是空的,那么这些号码会被放回袋子里。
...从这里,我可以创建一种各种算法。
<强>算法强>
行李通常是Collection
。我选择了bool[]
(也称为布尔数组,位平面或位图)来扮演角色。
我选择bool[]
的原因是因为每个项目的索引已经是每张纸的价值。如果论文要求在其上写下任何其他内容,那么我会在其位置使用Dictionary<string, bool>
。布尔值用于跟踪数字是否已被绘制。
名为RemainingNumberCount
的计数器初始化为5
,该计数器会在选择随机数时倒计时。这使我们无需计算每次我们希望绘制新数字时剩余的纸张数量。
要选择下一个随机值,我使用for..loop
来浏览索引包,并选择一个计数器,以便在index
false
调用时进行计数{ {1}}。
NumberOfMoves
用于选择下一个可用号码。 NumberOfMoves
首先设置为NumberOfMoves
和0
之间的随机值,因为我们可以通过行李进行0..5个可用步骤。在下一次迭代中,5
被设置为NumberOfMoves
和0
之间的随机值,因为现在我们可以通过包进行0..4步骤。在使用这些数字时,可用数字会减少,因此我们使用4
来计算rand() % (RemainingNumberCount + 1)
的下一个值。
当NumberOfMoves
计数器达到零时,NumberOfMoves
应如下所示:
for..loop
索引相同。for..loop
。false
。<强>代码强>
上述解决方案的代码如下:
(将以下三个块一个接一个地放入主.cpp文件中)
for..loop
控制台类
我创建此Console类,因为它可以轻松地重定向输出。
下面的代码......
#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;
}
};
...可以替换为......
Console::WriteLine("The next value is " + randomBag.ToString());
...如果需要,可以删除此std::cout << "The next value is " + randomBag.ToString() << std::endl;
类。
Console
主要方法
示例用法如下:
class Console {
public:
static void WriteLine(std::string s) {
std::cout << s << std::endl;
}
};
示例输出
当我运行程序时,我得到以下输出:
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");
}
结束语
此程序是使用 Visual Studio 2017 编写的,我选择使用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 . . .
将其设为Visual C++ Windows Console Application
项目。
我没有做任何特别特别的事情,所以代码也适用于早期版本的Visual Studio。
答案 10 :(得分:-1)
这是一个简单的随机发生器,约有。在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;
}
答案 11 :(得分:-1)
此代码产生从n
到m
的随机数。
int random(int n, int m){
return rand() % (m - n + 1) + n;
}
示例:
int main(){
srand(time(0));
for(int i = 0; i < 10; i++)
cout << random(0, 99);
}