我尝试着创造一款带有骰子的游戏,我需要在其中添加一些随机数字(游戏邦注:即模拟骰子的边缘)。我知道如何在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次生成相同的随机数。是否有这样的命令?


当前回答

非常固执己见的回答

c++ <random>库违反了软件工程的最佳原则之一:“简单的事情做简单,复杂的事情,不寻常的事情可以更复杂一点。”

相反,他们甚至把简单和常见的用例变得过于复杂,只是因为他们患有文化疾病,害怕像“这还不够普遍”这样的评论。

As a result, now whenever you want a simple random number, you have to look into the documentation, read stack overflow with walls of text, glorifying this terrible design, instead of it just being an easy-to-remember one or 2 liner. (Common Lisp is more pragmatic: (random 5) yields uniformly distributed integers from 0..4 and (random 1.0) yields real numbers between 0.0..1.0. That is the most common use case and it is at your finger tips. If you need more sophisticated stuff, you have to find packages and libraries or do it yourself.)

只需计算一下全球范围内每个人浪费在理解标题及其内容上的时间累积的工时,就可以看到它有多糟糕。

即使我现在在浪费我的时间,写这个答案,你也在浪费时间,阅读它,只是因为他们创造了一个复杂的谜题,这与其他现代令人厌恶的东西相似,比如Vulkan API。

那么,如何应对呢?浪费一次时间,为自己最常见的用例编写一个头文件,然后在需要时重用它。

其他回答

每当你在c++编程语言中做一个基本的随机数生成的web搜索时,这个问题通常是第一个跳出来的!我希望能够更好地阐明c++中伪随机数生成的概念,以便将来在web上不可避免地搜索相同的问题!

最基本的

伪随机数生成涉及利用确定性算法生成性质近似于随机数的数字序列的过程。我之所以说近似,是因为真正的随机性在数学和计算机科学中是一个相当难以捉摸的谜。因此,为什么术语伪随机被用来更学究的正确!

在真正使用PRNG(即伪随机数生成器)之前,必须为算法提供一个初始值,通常也称为种子。然而,在使用算法本身之前,种子必须只设置一次!

/// 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头文件中所谓的线性同余生成器!这个PRNG通过一个不连续分段函数来实现功能,该函数利用了模算术,即一个喜欢使用模运算符“%”的快速算法。以下是这个PRNG的常用用法,针对@ predictable最初提出的问题:

#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的常见用法包含了一大堆问题,例如:

The overall interface of std::rand() isn't very intuitive for the proper generation of pseudo-random numbers between a given range, e.g., producing numbers between [1, 6] the way @Predictability wanted. The common usage of std::rand() eliminates the possibility of a uniform distribution of pseudo-random numbers, because of the Pigeonhole Principle. The common way std::rand() gets seeded through std::srand( ( unsigned int )std::time( nullptr ) ) technically isn't correct, because time_t is considered to be a restricted type. Therefore, the conversion from time_t to unsigned int is not guaranteed!

有关使用C的PRNG的总体问题以及如何规避它们的更详细信息,请参阅using rand() (C/ c++): C标准库的rand()函数的建议!

标准c++方式

自从ISO/IEC 14882:2011标准发布以来,即c++ 11,随机库已经从c++编程语言中分离出来一段时间了。该库配备了多个prng,以及不同的分布类型,如:均匀分布,正态分布,二项分布等。下面的源代码示例演示了随机库的一个非常基本的用法,关于@ predictable的原始问题:

#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引擎在上面的例子中使用了整数值的均匀分布。(源代码中的引擎名称听起来很奇怪,因为它的名称来自于它的周期2^19937-1)。该示例还使用std::random_device为引擎提供种子,该引擎从操作系统获取其值(如果您使用的是Linux系统,则std::random_device从/dev/urandom返回一个值)。

请注意,您不必使用std::random_device来为任何引擎播种。您可以使用常量甚至chrono库!你也不必使用32位版本的std::mt19937引擎,还有其他选项!有关随机库功能的更多信息,请参阅cplusplus.com

总而言之,c++程序员不应该再使用std::rand(),不是因为它不好,而是因为当前的标准提供了更好的替代方案,更直接、更可靠。希望这篇文章对你们有帮助,特别是那些最近在网上搜索过c++生成随机数的人!

为每个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);
}

这是一个简单的随机生成器。在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;
   }

这段代码产生从n到m的随机数。

int random(int from, int to){
    return rand() % (to - from + 1) + from;
}

例子:

int main(){
    srand(time(0));
    cout << random(0, 99) << "\n";
}

可以从这里获得生成随机数的完整随机类代码!

如果你在项目的不同部分需要随机数,你可以创建一个单独的类Randomer来封装它里面的所有随机内容。

就像这样:

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";
}

你可以检查这个链接作为一个例子,我如何使用这样的Randomer类生成随机字符串。如果你愿意,你也可以使用Randomer。