当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《在整个范围内均匀生成随机数》经验,为你挑选了7个好方法。

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

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

Currenly我正在生成:

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平台上.有没有其他方法来生成具有均匀分布的随机数?



1> Shoe..:

为什么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  distr(range_from, range_to);

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

而这里的运行实例.

其他随机发电机

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

我怎样才能洗个容器?

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

std::vector 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.


请注意这个答案,因为它更加现代化.

2> peterchen..:

[编辑] 警告:不要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 + 1)是一个坏主意.这可以翻转并给你一个负值.最好做一些事情:((double)RAND_MAX)+ 1.0
即使所需范围不超过RAND_MAX,BigRand也能提供更好的结果.考虑当RAND_MAX是32767并且您想要32767个可能的值时 - 32768个随机数中的两个(包括零)将映射到相同的输出,并且其发生的可能性是其他输出的两倍.几乎没有理想的随机属性!
@peterchen:我觉得你误解了黛米说的话.她的意思是:`(rand()/((double)RAND_MAX + 1))*(max-min + 1)+ min`简单地将转换移动到双倍并避免问题.
此外,这仅仅将该范围内的底部32767值的分布改变为该范围内均匀分布的32767值,并且该算法将永远不会选择剩余的4017233值.

3> Alberto M..:

我想通过对2015年最新技术的简要概述来补充Angry Shoe和peterchen的优秀答案:

一些不错的选择

randutils

randutils库(演示)是一个有趣的新颖性,提供一个简单的界面和(声明)强大的随机能力.它的缺点在于它增加了对项目的依赖性,而且新的,它还没有经过广泛的测试.无论如何,免费(麻省理工学院许可证)和仅限标题,我认为值得一试.

最小样品:模具辊

#include 
#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的库,与其共享大部分接口.虽然理论上也是外部依赖,但Boost现在已经成为"准标准"库的一种状态,其随机模块可以被视为优质随机数生成的经典选择.它具有两个与C++ 11解决方案相关的优点:

它更易于移植,只需要编译器支持C++ 03

random_device使用系统特定的方法来提供高质量的播种

唯一的小缺陷是模块产品random_device不是仅仅头文件,必须编译和链接boost_random.

最小样品:模具辊

#include 
#include 
#include 

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 mt19937a thread_local:发生器非常丰满(> 2 KB),最好不要在堆栈上分配

mt19937具有多个整数的种子:Mersenne Twister具有较大的状态,并且在初始化期间可以利用更多的熵

一些不那么好的选择

C++ 11库

虽然是最惯用的解决方案,但即使对于基本需求,图书馆也没有提供太多交换其界面的复杂性.这个安全漏洞是std::random_device:该标准没有强制要求其输出的任何最小质量(只要entropy()回报率0)和截至2015年,MinGW的(不是最常用的编译器,但几乎没有一个esoterical选择)将始终打印4在最小的样品.

最小样品:模具辊

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

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

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

戈多的解决方案

最小样品:模具辊

#include 
#include 

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

这是一个简单,有效和简洁的解决方案.只有缺陷,编译需要一段时间 - 大约两年,提供C++ 17按时发布,实验randint功能被批准进入新标准.也许到那个时候,播种质量的保证也会提高.

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

最小样品:模具辊

#include 
#include 
#include 

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

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

会计巨魔解决方案

最小样品:模具辊

#include 

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

虽然9对于普通模具来说有点不同寻常,但人们不得不钦佩这种解决方案中优质品质的完美结合,它可以成为最快,最简单,最便于缓存和最便携的解决方案.通过替换9与4人得到一个完美的生成任何类型的龙与地下城死亡,同时还避免了符号载货值1,2和3.唯一的小缺点是,由于Dilbert的会计巨魔的坏脾气,这个程序实际上会产生未定义的行为.



4> Mark Ransom..:

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

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



5> Jeff Thomas..:

如果你能够,使用Boost.我对他们的随机库运气不错.

uniform_int 应该做你想做的事.



6> SoapBox..:

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

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


为什么不能使用第三方代码?如果这是一个家庭作业问题,你应该这样说,因为很多人宁愿提供有用的提示,而不是在这种情况下提供完整的解决方案.如果它不是一个家庭作业,去踢那个说"没有第三方代码"的人,因为他是个白痴.
RC4对代码来说是微不足道的,并且随机足以用于所有实际目的(除了WEP,但这并不完全是RC4的错).我的意思是,它是令人难以置信的微不足道的代码.比如,20行左右.维基百科条目具有伪代码.

7> abelenky..:

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

我不能保证这是答案,但请发布您的RAND_MAX值,并详细了解您的环境.

推荐阅读
小妖694_807
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有