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

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

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

目前我正在生成:

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

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

Example
min = 3604607;
max = 7654607;

产生的随机数:

3631594
3609293
3630000
3628441
3636376
3621404

从下面的答案: 好的,RAND _ MAX 是32767。我使用的是 C + + Windows 平台。有没有其他方法可以产生均匀分布的随机数?

153020 次浏览

如果希望数字在范围内均匀分布,应该将范围分成若干相等的部分,这些部分表示所需的点数。然后为每个部分获得一个最小/最大值的随机数。

作为另一个注意事项,您可能不应该使用 rand(),因为它不太擅长实际生成随机数。我不知道您在什么平台上运行,但是可能有一个更好的函数可以像 random()那样调用。

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

除此之外,请参阅以下讨论: Generating Random Integers within a Desired Range和关于使用(或不使用) C rand ()函数的说明。

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 的 随机数发生器.

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

对于特定的编译器/环境,应该查看 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为32767,则可以轻松地将位数增加一倍。

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

我刚在网上找到这个,应该可以:

DWORD random = ((min) + rand()/(RAND_MAX + 1.0) * ((max) - (min) + 1));

Man 3 Rand给出的1到10之间的数字的解决方案是:

j = 1 + (int) (10.0 * (rand() / (RAND_MAX + 1.0)));

对你来说,应该是:

j = min + (int) ((max-min+1) * (rand() / (RAND_MAX + 1.0)));

Of course, this is not perfect randomness or uniformity as some other messages are pointing out, but this is enough for most cases.

警告: 不要使用 rand()进行统计、模拟、加密或任何严重的操作。

对于一个典型的匆忙的人类来说,它足以使数字 听着随机,仅此而已。

有关更好的选项,请参见 杰弗里的回复,有关加密安全的随机数,请参见 this answer


一般来说,高位比低位显示出更好的分布,因此为了简单的目的,推荐的生成一个范围的随机数的方法是:

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

注意 : 确保 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.

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


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

如果你能够,使用 加油。我有好运与他们的 随机图书馆

uniform_int应该做你想做的。

Why rand is a bad idea

Most of the answers you got here make use of the rand function and the modulus operator. That method 可能不会产生一致的数字 (it depends on the range and the value of RAND_MAX), and is therefore discouraged.

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:

#include <iostream>
#include <random>
int main()
{
const int range_from  = 0;
const int range_to    = 1000;
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';
}

Try it online on Godbolt

给你就是运行中的例子。

Template function may help some:

template<typename T>
T random(T range_from, T range_to) {
std::random_device                  rand_dev;
std::mt19937                        generator(rand_dev());
std::uniform_int_distribution<T>    distr(range_from, range_to);
return distr(generator);
}

其他随机发生器

<random>提供了数不清的其他随机数生成器,它们具有不同的分布类型,包括伯努利分布、泊松分布和正态分布。

我怎样洗一个容器?

该标准提供了 std::shuffle,可用于以下方面:

#include <iostream>
#include <random>
#include <vector>
int main()
{
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);
std::for_each(vec.begin(), vec.end(), [](auto i){std::cout << i << '\n';});
}

在戈德博尔特网上试试

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

推进,随机

另一种选择是使用 推进,随机,以防您无法访问 C + + 11 + 编译器。它的接口非常类似于 C + + 11。

一个解决方案

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

警告 : 不要忘记由于拉伸和可能的精度错误(即使 RAND _ MAX 足够大) ,您只能生成均匀分布的“箱子”,而不能生成[ min,max ]中的所有数字。


A solution using Bigrand

警告 : 请注意,这将使位数增加一倍,但仍然不能生成一般范围内的所有数字,也就是说,BigRand ()不一定会生成其范围内的所有数字。


信息 : 只要 rand ()的范围超过区间范围并且 rand ()是“统一的”,您的方法(模)就是“好的”。第一个 max-min 数的最大误差为1/(RAND _ MAX + 1)。

此外,我建议在 C + + 11中也切换到新的 随机包裹,它提供了比 rand ()更好、更多样的实现。

这不是代码,但是这个逻辑可以帮助你。

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

只要整个范围小于 RAND _ MAX,就应该在不使用浮点数的情况下在 [low, high)范围内提供统一的分布。

uint32_t rand_range_low(uint32_t low, uint32_t high)
{
uint32_t val;
// only for 0 < range <= RAND_MAX
assert(low < high);
assert(high - low <= RAND_MAX);


uint32_t range = high-low;
uint32_t scale = RAND_MAX/range;
do {
val = rand();
} while (val >= scale * range); // since scale is truncated, pick a new val until it's lower than scale*range
return val/scale + low;
}

对于大于 RAND _ MAX 的值,需要类似

uint32_t rand_range(uint32_t low, uint32_t high)
{
assert(high>low);
uint32_t val;
uint32_t range = high-low;
if (range < RAND_MAX)
return rand_range_low(low, high);
uint32_t scale = range/RAND_MAX;
do {
val = rand() + rand_range(0, scale) * RAND_MAX; // scale the initial range in RAND_MAX steps, then add an offset to get a uniform interval
} while (val >= range);
return val + low;
}

这大致就是 std: : united _ int _ distribution 做事情的方式。

我想补充 鞋店Peterchen 的回答很棒的一个简短的概述,2015年的艺术状态:

不错的选择

randutils

randutils(简报)是一个有趣的创新,它提供了一个简单的接口和(声明的)健壮的随机功能。它的缺点是它会增加对项目的依赖性,而且由于它是新的,所以还没有经过广泛的测试。无论如何,作为自由(麻省理工学院执照)和头球,我认为这是值得一试。

最小样品: 模具辊

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

即使人们对这个库不感兴趣,网站也提供了许多有关随机数生成主题的有趣文章,特别是 C + + 库。

推进,随机

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

  • 它的可移植性更强,只是需要对 C + + 03的编译器支持
  • 它的 random_device使用系统特定的方法提供良好的质量播种

唯一的小缺陷是提供 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';
}

虽然最小的示例工作得很好,但是真正的程序应该使用一对改进:

  • 使 mt19937成为 thread_local: 生成器非常丰满(超过2KB) ,最好不要在堆栈上分配
  • 具有多个整数的种子 mt19937: 梅森旋转算法具有较大的状态,在初始化过程中可以利用更多的熵

一些不太好的选择

C + + 11库

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

最小样品: 模具辊

#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.

戈多的解决方案

Minimal sample: a die roll

#include <iostream>
#include <random>


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

这是一个简单、有效和巧妙的解决方案。唯一的缺陷是它需要一段时间来编译-大约两年,前提是 C + + 17按时发布,而且实验性的 randint函数被批准成为新的标准。也许到那时,对播种质量的保证也会提高。

越坏越好溶液

最小样品: 模具辊

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


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

旧的 C 解决方案被认为是有害的,而且有充分的理由(请参阅这里的其他答案或 这个详细的分析)。尽管如此,它还是有它的优势: 简单、便携、快速和诚实,因为人们知道随机数几乎不可能像样,因此人们不会将它们用于严肃的目的。

会计巨魔解决方案

最小样品: 模具辊

#include <iostream>


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

虽然9是一个有点不寻常的结果,定期骰子辊,一个不得不佩服这个解决方案的良好品质的优秀组合,这设法是最快,最简单,最缓存友好和最便携的一个。通过用4代替9,可以得到任何类型的 龙与地下城死亡的完美生成器,同时还可以避免满足符号的值1、2和3。唯一的小瑕疵是,由于 翻译: 奇芳翻译: 奇芳奇芳奇芳奇芳奇芳奇芳奇芳奇芳奇芳奇芳奇芳奇芳奇芳奇芳奇芳奇芳奇芳奇芳奇芳奇芳奇芳奇芳奇芳奇芳奇芳奇芳奇芳奇芳奇芳奇芳会计巨魔的坏脾气,这个程序实际上产生了未定义行为。

当然,下面的代码不会给出随机数,而是伪随机数。

使用以下代码

#define QUICK_RAND(m,n) m + ( std::rand() % ( (n) - (m) + 1 ) )

例如:

int myRand = QUICK_RAND(10, 20);

你必须打电话

srand(time(0));  // Initialize random number generator.

否则这些数字不会是随机的。

这是我想到的解决办法:

#include "<stdlib.h>"


int32_t RandomRange(int32_t min, int32_t max) {
return (rand() * (max - min + 1) / (RAND_MAX + 1)) + min;
}

This is a bucket solution, conceptually similar to the solutions that use rand() / RAND_MAX to get a floating point range between 0-1 and then round that into a bucket. However, it uses purely integer math, and takes advantage of integer division flooring to round down the value to the nearest bucket.

它做了一些假设。首先,它假设 RAND_MAX * (max - min + 1)始终适合于 int32_t。如果使用 RAND_MAX为32767并使用32位 int 计算,则最大范围为32767。如果您的实现有一个更大的 RAND _ MAX,您可以通过使用一个更大的整数(如 int64_t)进行计算来克服这个问题。其次,如果使用 int64_t,但 RAND_MAX仍然是32767,在范围大于 RAND_MAX你将开始得到“孔”在可能的输出数字。这可能是从扩展 rand()衍生的任何解决方案的最大问题。

然而,通过大量迭代进行的测试表明,这种方法对于小范围来说是非常统一的。然而,这是可能的(并且很有可能) ,在数学上这有一些小的偏差,并且当范围接近 RAND_MAX时可能发生问题。自己测试一下,看看是否能满足你的需要。

使用 梅森旋转算法引擎(C + + 11) :

#include <random>


// Returns a random integer within the range [min, max]
int generateRandomInt(const int min, const int max) {
static bool is_seeded = false;
static std::mt19937 generator;


// Seed once
if (!is_seeded) {
std::random_device rd;
generator.seed(rd());
is_seeded = true;
}


// Use a Mersenne Twister engine to pick a random number
// within the given range
std::uniform_int_distribution<int> distribution(min, max);
return distribution(generator);
}

C + + 11的最小实现:

#include <random>


int randrange (int min, int max) {
static std::random_device rd; // Static in case init is costly
return std::uniform_int_distribution {min, max} (rd);
}