在整个范围内均匀生成随机数
我需要在指定的时间间隔内产生随机数,[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));