C++ 在整个范围内均匀地生成随机数

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/288739/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 14:22:06  来源:igfitidea点击:

Generate random numbers uniformly over an entire range

c++random

提问by anand

I need to generate random numbers within a specified interval, [max;min].

我需要在指定的时间间隔内生成随机数,[max;min]。

Also, the random numbers should be uniformly distributed over the interval, not located to a particular point.

此外,随机数应该在区间内均匀分布,而不是定位到特定点。

Currenly I am generating as:

目前我生成为:

for(int i=0; i<6; i++)
{
    DWORD random = rand()%(max-min+1) + min;
}

From my tests, random numbers are generated around one point only.

根据我的测试,随机数仅在一点附近生成。

Example
min = 3604607;
max = 7654607;

Random numbers generated:

随机数生成:

3631594
3609293
3630000
3628441
3636376
3621404

From answers below: OK, RAND_MAX is 32767. I am on C++ Windows platform. Is there any other method to generate random numbers with a uniform distribution?

从下面的答案:好的,RAND_MAX 是 32767。我在 C++ Windows 平台上。还有其他方法可以生成均匀分布的随机数吗?

回答by Shoe

Why randis a bad idea

为什么rand是个坏主意

Most of the answers you got here make use of the randfunction and the modulus operator. That method may not generate numbers uniformly(it depends on the range and the value of RAND_MAX), and is therefore discouraged.

您在这里得到的大多数答案都使用了rand函数和模运算符。该方法可能不会统一生成数字(它取决于 的范围和值RAND_MAX),因此不鼓励使用。

C++11 and generation over a range

C++11 和范围内的生成

With C++11 multiple other options have risen. One of which fits your requirements, for generating a random number in a range, pretty nicely: std::uniform_int_distribution. Here's an example:

在 C++11 中,出现了多种其他选择。其中之一符合您的要求,用于生成范围内的随机数,非常好:std::uniform_int_distribution. 下面是一个例子:

const int range_from  = 0;
const int range_to    = 10;
std::random_device                  rand_dev;
std::mt19937                        generator(rand_dev());
std::uniform_int_distribution<int>  distr(range_from, range_to);

std::cout << distr(generator) << '\n';

And here's the running example.

这里的运行实例。

Other random generators

其他随机发生器

The <random>headeroffers innumerable other random number generators with different kind of distributions including Bernoulli, Poisson and normal.

<random>提供了无数的其他随机数生成与不同类型的分布,包括伯努利,泊松分布和正常的。

How can I shuffle a container?

我怎样才能洗牌一个容器?

The standard provides std::shuffle, which can be used as follows:

标准提供了std::shuffle,可以如下使用:

std::vector<int> vec = {4, 8, 15, 16, 23, 42};

std::random_device random_dev;
std::mt19937       generator(random_dev());

std::shuffle(vec.begin(), vec.end(), generator);

The algorithm will reorder the elements randomly, with a linear complexity.

该算法将随机重新排序元素,具有线性复杂度。

Boost.Random

Boost.Random

Another alternative, in case you don't have access to a C++11+ compiler, is to use Boost.Random. Its interface is very similar to the C++11 one.

如果您无法访问 C++11+ 编译器,另一种选择是使用Boost.Random。它的界面与 C++11 非常相似。

回答by peterchen

[edit]Warning: Do not use rand()for statistics, simulation, cryptography or anything serious.

[编辑]警告:不要rand()用于统计、模拟、密码学或任何严重的事情。

It's good enough to make numbers lookrandom for a typical human in a hurry, no more.

对于一个匆忙的普通人来说,让数字看起来随机就足够了,仅此而已。

See @Jefffrey's replyfor better options, or this answerfor crypto-secure random numbers.

请参阅@Jeffrey 的回复以获得更好的选择,或此答案为加密安全随机数。



Generally, the high bits show a better distribution than the low bits, so the recommended way to generate random numbers of a range for simple purposes is:

通常,高位比低位显示出更好的分布,因此出于简单目的生成范围的随机数的推荐方法是:

((double) rand() / (RAND_MAX+1)) * (max-min+1) + min

Note: make sure RAND_MAX+1 does not overflow (thanks Demi)!

注意:确保 RAND_MAX+1 不​​会溢出(感谢 Demi)!

The division generates a random number in the interval [0, 1); "stretch" this to the required range. Only when max-min+1 gets close to RAND_MAX you need a "BigRand()" function like posted by Mark Ransom.

除法在区间[0, 1)内产生一个随机数;“拉伸”到所需的范围。只有当 max-min+1 接近 RAND_MAX 时,您才需要像 Mark Ransom 发布的“BigRand()”函数。

This also avoids some slicing problems due to the modulo, which can worsen your numbers even more.

这也避免了由于模数导致的一些切片问题,这会使您的数字更加恶化。



The built-in random number generator isn't guaranteed to have a the quality required for statistical simulations. It is OK for numbers to "look random" to a human, but for a serious application, you should take something better - or at least check its properties (uniform distribution is usually good, but values tend to correlate, and the sequence is deterministic). Knuth has an excellent (if hard-to-read) treatise on random number generators, and I recently found LFSRto be excellent and darn simple to implement, given its properties are OK for you.

内置的随机数生成器不能保证具有统计模拟所需的质量。数字对人类来说“看起来随机”是可以的,但是对于严肃的应用程序,您应该采取更好的方法 - 或者至少检查它的属性(均匀分布通常是好的,但值往往相关,并且序列是确定性的)。Knuth 有一篇关于随机数生成器的优秀(如果难以阅读)论文,我最近发现LFSR非常好,而且实现起来非常简单,因为它的属性对你来说还可以。

回答by Alberto M

I'd like to complement Angry Shoe's and peterchen's excellent answers with a short overview of the state of the art in 2015:

我想通过对 2015 年最新技术的简短概述来补充 Angry Shoe 和 peterchen 的出色回答:

Some good choices

一些不错的选择

randutils

randutils

The randutilslibrary (presentation)is an interesting novelty, offering a simple interface and (declared) robust random capabilities. It has the disadvantages that it adds a dependence on your project and, being new, it has not been extensively tested. Anyway, being free (MIT license) and header-only, I think it's worth a try.

randutils(演示文稿)是一个有趣的新奇事物,它提供了一个简单的界面和(声明的)强大的随机功能。它的缺点是它增加了对您的项目的依赖,并且是新的,它没有经过广泛的测试。无论如何,免费(MIT 许可)和仅标头,我认为值得一试。

Minimal sample: a die roll

最小样本:模具卷

#include <iostream>
#include "randutils.hpp"
int main() {
    randutils::mt19937_rng rng;
    std::cout << rng.uniform(1,6) << "\n";
}

Even if one is not interested in the library, the website (http://www.pcg-random.org/) provides many interesting articles about the theme of random number generation in general and the C++ library in particular.

即使对该库不感兴趣,该网站 ( http://www.pcg-random.org/) 也提供了许多关于一般随机数生成主题,特别是 C++ 库的有趣文章。

Boost.Random

Boost.Random

Boost.Random(documentation)is the library which inspired C++11's <random>, with whom shares much of the interface. While theoretically also being an external dependency, Boosthas by now a status of "quasi-standard" library, and its Randommodule could be regarded as the classical choice for good-quality random number generation. It features two advantages with respect to the C++11solution:

Boost.Random(文档)是启发C++11's的库<random>,与它共享大部分界面。虽然理论上也是一个外部依赖,但Boost目前已经具有“准标准”库的地位,其Random模块可以被视为高质量随机数生成的经典选择。它在C++11解决方案方面具有两个优点:

  • it is more portable, just needing compiler support for C++03
  • its random_deviceuses system-specific methods to offer seeding of good quality
  • 它更便携,只需要编译器支持 C++03
  • random_device使用系统特定的方法来提供高质量的播种

The only small flaw is that the module offering random_deviceis not header-only, one has to compile and link boost_random.

唯一的小缺陷是模块提供random_device的不是仅标头,必须编译和链接boost_random.

Minimal sample: a die roll

最小样本:模具卷

#include <iostream>
#include <boost/random.hpp>
#include <boost/nondet_random.hpp>

int main() {
    boost::random::random_device                  rand_dev;
    boost::random::mt19937                        generator(rand_dev());
    boost::random::uniform_int_distribution<>     distr(1, 6);

    std::cout << distr(generator) << '\n';
}

While the minimal sample does its work well, real programs should use a pair of improvements:

虽然最小样本可以很好地工作,但真正的程序应该使用以下两个改进:

  • make mt19937a thread_local: the generator is quite plump (> 2 KB) and is better not allocated on the stack
  • seed mt19937with more than one integer: the Mersenne Twister has a big state and can take benefit of more entropy during initialization
  • make mt19937a thread_local:生成器非常丰满(> 2 KB),最好不要在堆栈上分配
  • mt19937具有多个整数的种子:Mersenne Twister 有一个很大的状态,并且可以在初始化期间利用更多的熵

Some not-so-good choices

一些不太好的选择

The C++11 library

C++11 库

While being the most idiomatic solution, the <random>library does not offer much in exchange for the complexity of its interface even for the basic needs. The flaw is in std::random_device: the Standard does not mandate any minimal quality for its output (as long as entropy()returns 0) and, as of 2015, MinGW (not the most used compiler, but hardly an esoterical choice) will always print 4on the minimal sample.

虽然是最惯用的解决方案,但<random>即使是基本需求,该库也没有提供太多以换取其接口的复杂性。缺陷在于std::random_device:标准不要求其输出的任何最低质量(只要entropy()返回0),并且截至 2015 年,MinGW(不是最常用的编译器,但几乎不是一个深奥的选择)将始终打印4在最小样本上。

Minimal sample: a die roll

最小样本:模具卷

#include <iostream>
#include <random>
int main() {
    std::random_device                  rand_dev;
    std::mt19937                        generator(rand_dev());
    std::uniform_int_distribution<int>  distr(1, 6);

    std::cout << distr(generator) << '\n';
}

If the implementation is not rotten, this solution should be equivalent to the Boost one, and the same suggestions apply.

如果实现不烂,这个解决方案应该等同于 Boost 一个,同样的建议也适用。

Godot's solution

戈多的解决方案

Minimal sample: a die roll

最小样本:模具卷

#include <iostream>
#include <random>

int main() {
    std::cout << std::randint(1,6);
}

This is a simple, effective and neat solution. Only defect, it will take a while to compile – about two years, providing C++17 is released on time and the experimental randintfunction is approved into the new Standard. Maybe by that time also the guarantees on the seeding quality will improve.

这是一个简单、有效和整洁的解决方案。只是有缺陷,编译需要一段时间——大约两年,前提是 C++17 按时发布,并且实验randint功能被批准到新标准中。也许到那个时候,播种质量的保证也会得到改善。

The worse-is-bettersolution

糟糕的就是更好的解决方案

Minimal sample: a die roll

最小样本:模具卷

#include <cstdlib>
#include <ctime>
#include <iostream>

int main() {
    std::srand(std::time(nullptr));
    std::cout << (std::rand() % 6 + 1);
}

The old C solution is considered harmful, and for good reasons (see the other answers here or this detailed analysis). Still, it has its advantages: is is simple, portable, fast and honest, in the sense it is known that the random numbers one gets are hardly decent, and therefore one is not tempted to use them for serious purposes.

旧的 C 解决方案被认为是有害的,并且有充分的理由(请参阅此处的其他答案或此详细分析)。尽管如此,它也有它的优点:简单、便携、快速和诚实,从某种意义上说,人们知道人们得到的随机数很难得体,因此人们不会试图将它们用于严肃的目的。

The accounting troll solution

会计巨魔解决方案

Minimal sample: a die roll

最小样本:模具卷

#include <iostream>

int main() {
    std::cout << 9;   // http://dilbert.com/strip/2001-10-25
}

While 9 is a somewhat unusual outcome for a regular die roll, one has to admire the excellent combination of good qualities in this solution, which manages to be the fastest, simplest, most cache-friendly and most portable one. By substituting 9 with 4 one gets a perfect generator for any kind of Dungeons and Dragons die, while still avoiding the symbol-laden values 1, 2 and 3. The only small flaw is that, because of the bad temper of Dilbert's accounting trolls, this program actually engenders undefined behavior.

虽然 9 对常规掷骰子来说是一个有点不寻常的结果,但人们不得不欣赏这个解决方案中优良品质的出色组合,它设法成为最快、最简单、最适合缓存和最便携的解决方案。通过将 9 替换为 4,一个完美的生成器可以生成任何类型的龙与地下城之死,同时仍然避免符号负载值 1、2 和 3。唯一的小缺陷是,由于 Dilbert 的会计巨魔脾气暴躁,该程序实际上会产生未定义的行为。

回答by Mark Ransom

If RAND_MAXis 32767, you can double the number of bits easily.

如果RAND_MAX是 32767,则可以轻松地将位数加倍。

int BigRand()
{
    assert(INT_MAX/(RAND_MAX+1) > RAND_MAX);
    return rand() * (RAND_MAX+1) + rand();
}

回答by Jeff Thomas

If you are able to, use Boost. I have had good luck with their random library.

如果可以,请使用Boost。我对他们的随机库祝你好运。

uniform_intshould do what you want.

uniform_int应该做你想做的。

回答by SoapBox

If you are concerned about randomness and not about speed, you should use a secure random number generation method. There are several ways to do this... The easiest one being to use OpenSSL'sRandom Number Generator.

如果您关心随机性而不是速度,您应该使用安全的随机数生成方法。有几种方法可以做到这一点......最简单的一种是使用OpenSSL 的Random Number Generator

You can also write your own using an encryption algorithm (like AES). By picking a seed and an IVand then continuously re-encrypting the output of the encryption function. Using OpenSSL is easier, but less manly.

您还可以使用加密算法(如AES)编写自己的加密算法。通过选择一个种子和一个IV,然后不断地重新加密加密函数的输出。使用 OpenSSL 更容易,但不那么男子气概。

回答by abelenky

You should look at RAND_MAXfor your particular compiler/environment. I think you would see these results if rand()is producing a random 16-bit number. (you seem to be assuming it will be a 32-bit number).

您应该查看RAND_MAX您的特定编译器/环境。我认为如果rand()生成随机的 16 位数字,您会看到这些结果。(您似乎假设它将是一个 32 位数字)。

I can't promise this is the answer, but please post your value of RAND_MAX, and a little more detail on your environment.

我不能保证这就是答案,但请发布您的 值RAND_MAX,以及有关您的环境的更多详细信息。

回答by Rob Walker

Check what RAND_MAXis on your system -- I'm guessing it is only 16 bits, and your range is too big for it.

检查RAND_MAX你的系统上有什么——我猜它只有 16 位,而且你的范围太大了。

Beyond that see this discussion on: Generating Random Integers within a Desired Rangeand the notes on using (or not) the C rand() function.

除此之外,请参阅以下讨论:在所需范围内生成随机整数以及使用(或不使用)C rand() 函数的注意事项

回答by Jason Coco

If you want numbers to be uniformly distributed over the range, you should break your range up into a number of equal sections that represent the number of points you need. Then get a random number with a min/max for each section.

如果您希望数字在整个范围内均匀分布,您应该将您的范围分成多个相等的部分,代表您需要的点数。然后为每个部分获取一个具有最小值/最大值的随机数。

As another note, you should probably not use rand()as it's not very good at actually generating random numbers. I don't know what platform you're running on, but there is probably a better function you can call like random().

另请注意,您可能不应该使用rand()它,因为它实际上不太擅长生成随机数。我不知道您在哪个平台上运行,但可能有更好的函数可以调用,例如random().

回答by user3503711

This is not the code, but this logic may help you.

这不是代码,但此逻辑可能对您有所帮助。

static double rnd(void)
{
   return (1.0 / (RAND_MAX + 1.0) * ((double)(rand())) );
}

static void InitBetterRnd(unsigned int seed)
{
    register int i;
    srand( seed );
    for( i = 0; i < POOLSIZE; i++){
        pool[i] = rnd();
    }
}

 // This function returns a number between 0 and 1
 static double rnd0_1(void)
 {
    static int i = POOLSIZE-1;
    double r;

    i = (int)(POOLSIZE*pool[i]);
    r = pool[i];
    pool[i] = rnd();
    return (r);
}