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

117

我需要在指定的区间[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

下面的回答中提到:OK,RAND_MAX是32767。我在C++ Windows平台上。是否有其他方法可以生成具有均匀分布的随机数?


4
建造一个“骰子奇妙机器”: http://gamesbyemail.com/News/DiceOMatic - Jarett Millard
1
我不知道C++的rand()是均匀的。你使用的是哪个库?cstdlib.hrand()是不均匀的:http://www.cplusplus.com/reference/cstdlib/rand/ - Mike Warren
3
不,rand() 函数是均匀的(除非在一些早期有缺陷的实现中)。不均匀的是使用模运算符“%”来限制范围。请参考 https://dev59.com/p3A75IYBdhLWcg3w-OZA#2999130 获取正确的解决方案,或者如果您可以使用 'arc4random_uniform',则也可以直接使用它。 - John Meacham
@Alien01:你是否考虑将接受的答案更改为“Shoe”的答案(“为什么rand是一个坏主意”等)?我的答案已经过时,每次我因为它而获得赞时,我感觉有人在错误的通道奔跑。 - peterchen
关于C++11中随机数的白皮书非常不错。 - Pupsik
相关:JavaScript的对应规范问题 - Peter Mortensen
20个回答

2
如果你希望数字在范围内均匀分布,你应该将范围分成相等的部分,代表你需要的点数。然后为每个部分获取一个具有最小/最大值的随机数。
另外,你应该避免使用rand(),因为它不能很好地生成随机数。我不知道你在哪个平台上运行,但可能有更好的函数可供调用,比如random()。

2

这不是代码,但这个逻辑或许可以帮到你。

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

0

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

0

一个解决方案

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

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


使用Bigrand的解决方案

警告:请注意,这将使位数加倍,但一般情况下仍无法生成您范围内的所有数字,即不一定会真正生成BigRand()范围内的所有数字。


信息:只要rand()的范围超过您的区间范围并且rand()是“均匀”的,您的方法(模数)就“可以”。最多前max-min个数字的误差为1 /(RAND_MAX +1)。

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


0

从本质上讲,一小组随机数并不一定是均匀分布的。毕竟它们是随机的。我同意,如果一个随机数生成器生成的数字总是看起来被分组了,那么可能存在问题。

但请记住,随机性不一定是均匀的。


“均匀分布”有明确定义,标准随机发生器通常可以很好地接近这个定义。 - peterchen
是的,你说得对,随机数生成器应该产生随着时间的推移在其分布上通常是均匀的输出。我想我的观点是,在少量实例(如示例中的6个)中,输出不总是均匀的。 - Kluge
Kluge是正确的。 在小样本中出现均匀分布表明该样本绝对不是随机的。 - Bill the Lizard
1
Bill,它并没有表明任何事情。小样本大多数是毫无意义的,但如果随机数生成器应该是均匀的,而输出也是均匀的,那么这与非均匀小样本有什么区别呢? - Dan Dyer
2
任何明显的分布方式都表明不是随机的:我认为比尔只是指6个等间距的结果也有问题。 在原始帖子中,有6个值位于32k / 4M的范围内,或所需范围的<1%。 这种情况出现误报的概率太小了,无需争论。 - Steve Jessop
在那个范围内产生六个小于32767的均匀分布值的概率大约是0.000000000000000000000000000000007425%,不是吗?特别是考虑到我们除了偶然性之外还有另一种解释... - Mooing Duck

0

man 3 rand提供的在1到10之间(包含1和10)生成随机数的解决方案如下:

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

在你的情况下,它将是:
j = min + (int) ((max-min+1) * (rand() / (RAND_MAX + 1.0)));

当然,这并不是完美的随机性或均匀性,正如其他一些消息所指出的那样,但对于大多数情况来说,这已经足够了。


1
这只是重新排列分布以使其看起来更均匀,但对于大范围(例如OP的情况),实际上并没有更加均匀。 - Mooing Duck

0

这是一个旧的线程,但我刚刚偶然发现它。以下是我的看法:

正如其他人正确指出的那样,在大多数 RNG 中,MSB 倾向于比 LSB 更随机分布。这意味着使用 rand() 的模数(%)注定要失败:例如,极为频繁的 random(2) 只会返回单个 LSB... 这在大多数 RNG 中分布极差。 另一方面,如果你需要你的 random(N) 非常快速(就像我这样:我在 HPC 和特别是在高度随机化的 GAs 中),那么取模对于速度来说确实很酷。

以上两个问题都可以通过 (1) 计算 (快速) 取模... (2) rand() 的反转比特位 来解决。


0
这是我想出来的解决方案:
#include "<stdlib.h>"

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

这是一个桶解决方案,概念上类似于使用 rand() / RAND_MAX 来获取0-1之间的浮点数范围,然后将其四舍五入到一个桶中的解决方案。但是,它仅使用整数运算,并利用整数除法向下取整将值舍入到最近的桶。

它做出了一些假设。首先,它假设 RAND_MAX * (max - min + 1) 总是适合于 int32_t。如果 RAND_MAX 是32767并且使用32位int计算,则您可以拥有的最大范围是32767。如果您的实现具有更大的RAND_MAX,则可以通过使用更大的整数(如 int64_t)进行计算来克服这个问题。其次,如果使用 int64_tRAND_MAX 仍为32767,则在大于 RAND_MAX 的范围内,您将开始获得可能输出数字中的“空洞”。这可能是从缩放 rand() 派生的任何解决方案的最大问题。

尽管在大量迭代测试中,这种方法对于小范围非常均匀。但是从数学上来说,很可能存在一些小偏差,并且可能在范围接近RAND_MAX时出现问题。请自行测试并决定是否符合您的需求。


-1

当然,以下代码不会给你随机数,而是伪随机数。

使用以下代码

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

例如:

int myRand = QUICK_RAND(10, 20);

你必须调用

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

否则,这些数字就不会是近似随机的。

3
这个问题要求一个均匀分布。这个提出的解决方案不会产生均匀分布。标准C++库有Pseudo-random number generation功能,如果需要,可以提供均匀分布。 - IInspectable

-3

我刚在网上找到了这个。应该可以用:

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

1
请澄清您需要它们的用途,因为有大量的PRNG算法可供选择。此外,如果您编辑主要问题而不是发布回复,则会更容易。 - peterchen
这对我来说效果最好...我能够使用这个公式获得更好的分布式随机数.. - anand
5
如果你的范围超过了RAND_MAX,结果可能不会均匀。也就是说,在你调用函数多少次都无法表示范围内的某些值。注意,原文中“won't”是错误的,应该改为“will”。 - dmckee --- ex-moderator kitten
5
如果max和min都是无符号整数,且min为0,max为MAX_UINT,则((max)-(min)+1)将为0,并且结果始终为0。在进行此类计算时要注意溢出!正如dmckee所指出的那样,这会拉伸目标范围内的分布,但不保证超过RAND_MAX个唯一值。 - jesup

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接