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

我需要在指定的时间间隔内产生随机数,[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平台上。 有没有其他的方法来产生均匀分布的随机数字?

为什么rand是一个坏主意

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

C ++ 11和一个范围内的世代

随着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'; 

这就是运行的例子。

其他随机发生器

<random>头文件提供了无数其他随机数发生器,它们具有不同的分布类型,包括Bernoulli,Poisson和Normal。

我怎么洗一个容器?

该标准提供了std::random_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); 

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

Boost.Random

另一种方法是,如果你不能访问C ++ 11 +编译器,则使用Boost.Random 。 它的界面非常类似于C ++ 11。

警告:不要使用rand()进行统计,模拟,密码或任何严重的事情。

对于一个典型的人来说,数字看起来是随机的,不用多说了。

请参阅@ Jefffrey的回复以获得更好的选项,或者针对加密安全随机数字的答案 。


一般来说,高位比低位显示更好的分布,所以为了简单目的而推荐的产生范围的随机数的方式是:

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

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

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

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


内置的随机数发生器不能保证具有统计模拟所需的质量。 数字对于一个人来说是“随机的”是可以的,但是对于一个严肃的应用,你应该采取一些更好的方法,或者至少检查一下它的属性(均匀分布通常是好的,但是价值往往是相关的,序列是确定性的)。 Knuth在随机数字生成器方面有很好的(如果难以阅读的)论文,最近我发现LFSR是非常好的,并且由于它的属性对你来说是简单易行的。

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

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

如果你能够使用Boost 。 随机图书馆我运气不错。

uniform_int应该做你想要的。

如果您担心随机性而不是速度问题,则应该使用安全的随机数生成方法。 有几种方法可以做到这一点…最简单的就是使用OpenSSL的 随机数生成器 。

您也可以使用加密算法(如AES )编写自己的程序。 通过挑选种子和IV ,然后不断地重新加密加密函数的输出。 使用OpenSSL比较容易,但是不太方便。

我想补充愤怒的鞋和peterchen的优秀答案,并在2015年的艺术状况简短的概述:

一些不错的选择

randutils

randutils库(presentation)是一个有趣的新颖事物,提供了一个简单的接口和(声明的)强大的随机能力。 它的缺点是增加了对你的项目的依赖,而且是新的,它还没有经过广泛的测试。 无论如何,免费(MIT许可证)和标题,我认为这是值得一试。

最小的样品:一个模子卷

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

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

Boost.Random

Boost.Random (文档)是启发C ++ 11的<random> ,与其共享大量的接口。 在理论上也是一种外部依赖的情况下,Boost现在已经具有“准标准”库的地位,其随机模块可以被看作是优质随机数生成的经典选择。 它具有与C ++ 11解决方案相关的两个优点:

  • 它更便携,只需要C ++ 03的编译器支持
  • random_device使用系统特定的方法来提供优质的种子

唯一的小缺点是,提供random_device的模块不是头文件,需要编译并链接boost_random

最小的样品:一个模子卷

 #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'; } 

虽然最小的样本能够很好地工作,但真正的程序应该使用一对改进:

  • make mt19937一个thread_local :生成器相当丰满(> 2 KB),最好不要在堆栈上分配
  • 具有多个整数的种子mt19937 :Mersenne Twister具有较大的状态,可以在初始化期间利用更多的熵

一些不太好的选择

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

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

戈多的解决方案

最小的样品:一个模子卷

 #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.唯一的小缺点是,由于迪尔伯特会计巨魔的脾气暴躁,这个程序实际上会产生未定义的行为。

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

我不能保证这是答案,但请张贴您的RAND_MAX的价值,并在您的环境更详细一点。

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

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

如果你想让数字在整个范围内均匀分布,你应该把你的范围分成许多相等的部分,代表你需要的点数。 然后得到每个部分最小/最大值的随机数字。

作为另一个说明,你可能不应该使用rand(),因为它不是很好的实际生成随机数字。 我不知道你在运行什么平台,但是可能有一个更好的函数可以像random()那样调用。

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

 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(); } } static double rnd0_1(void) { // This function returns a number between 0 and 1 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 :: uniform_int_distribution做的事情。

就其性质而言,一小部分随机数不一定是均匀分布的。 毕竟,它们是随机的。 我同意,如果一个随机数字生成器生成的数字一直显示为分组,那么可能有些问题。

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

编辑:我添加了“小样本”澄清。

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

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

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

警告 :不要忘记,由于拉伸和可能的精度错误(即使RAND_MAX足够大),你将只能够生成均匀分布的“箱”,而不是所有的数字[最小,最大]。


@解决方案:Bigrand

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


信息 :只要rand()的范围超出你的区间范围,rand()是“uniform”,你的方法(模)就是“很好”的。 最多第一个最大 – 最小数字的错误是1 /(RAND_MAX + 1)。

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

我刚在互联网上发现了这个。 这应该工作:

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