108

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

4

18 回答 18

184

为什么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';

这是正在运行示例。

模板功能可能会有所帮助:

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,可按如下方式使用:

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 非常相似。

于 2013-11-22T03:04:49.137 回答
63

[编辑] 警告:不要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非常出色,而且实现起来非常简单,因为它的属性对你来说还可以。

于 2008-11-14T00:24:00.697 回答
18

我想通过简要概述 2015 年的最新技术来补充 Angry Shoe 和 peterchen 的出色回答:

一些不错的选择

randutils

randutils(演示文稿)是一个有趣的新奇事物,提供了一个简单的界面和(声明的)强大的随机功能。它的缺点是它增加了对您的项目的依赖,而且它是新的,还没有经过广泛的测试。无论如何,免费(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 (documentation)是启发C++11's的库<random>,与他共享大部分界面。虽然理论上也是一个外部依赖,Boost但目前已经处于“准标准”库的状态,其Random模块可以被视为高质量随机数生成的经典选择。就解决方案而言,它具有两个优点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 mt19937a 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。唯一的小缺陷是,由于呆伯特会计巨魔的坏脾气,该程序实际上会产生未定义的行为。

于 2015-12-16T16:13:15.453 回答
10

如果RAND_MAX是 32767,您可以轻松地将位数加倍。

int BigRand()
{
    assert(INT_MAX/(RAND_MAX+1) > RAND_MAX);
    return rand() * (RAND_MAX+1) + rand();
}
于 2008-11-13T23:44:16.533 回答
9

如果可以,请使用Boost。我对他们的随机图书馆很幸运。

uniform_int应该做你想做的。

于 2008-11-14T00:58:25.277 回答
8

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

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

于 2008-11-13T23:33:53.387 回答
5

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

我不能保证这就是答案,但请发布您的价值RAND_MAX,并在您的环境中提供更多详细信息。

于 2008-11-13T23:36:47.063 回答
3

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

除此之外,请参阅以下讨论:在所需范围内生成随机整数以及使用(或不使用)C rand() 函数的说明。

于 2008-11-13T23:33:16.650 回答
2

如果您希望数字在该范围内均匀分布,您应该将您的范围分成多个相等的部分,这些部分代表您需要的点数。然后为每个部分获取一个具有最小值/最大值的随机数。

另请注意,您可能不应该使用rand()它,因为它实际上并不擅长生成随机数。我不知道您在哪个平台上运行,但可能有一个更好的函数可以调用,例如random().

于 2008-11-13T23:32:03.703 回答
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);
}
于 2014-07-08T09:56:32.347 回答
2

[low, high)只要整个范围小于 RAND_MAX,这应该在不使用浮点数的情况下提供范围内的均匀分布。

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 做事的方式。

于 2015-06-09T16:58:28.510 回答
0

就其性质而言,随机数的小样本不必均匀分布。毕竟,它们是随机的。我同意,如果随机数生成器生成的数字始终似乎是分组的,那么它可能有问题。

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

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

于 2008-11-13T23:38:48.993 回答
0

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

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

于 2008-11-14T00:08:13.613 回答
0

@解决方案 ((double) rand() / (RAND_MAX+1)) * (max-min+1) + min

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


@解决方案:比格朗

警告:请注意,这会使位加倍,但通常仍无法生成您范围内的所有数字,即 BigRand() 不一定会生成其范围内的所有数字。


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

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

于 2014-01-31T11:11:06.643 回答
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_t使用了但RAND_MAX仍然是 32767,则在大于RAND_MAX您的范围内,您将开始在可能的输出数字中出现“漏洞”。这可能是任何源自缩放的解决方案的最大问题rand()

尽管如此,对大量迭代的测试表明,这种方法对于小范围非常统一。但是,从数学上讲,这有可能(并且很可能)有一些小的偏差,并且当范围接近时可能会产生问题RAND_MAX。自己测试并确定它是否满足您的需求。

于 2020-04-21T18:28:11.047 回答
0

使用 mersenne twister 引擎 (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 mersenne twister engine to pick a random number
  // within the given range
  std::uniform_int_distribution<int> distribution(min, max);
  return distribution(generator);
}
于 2022-01-08T06:45:47.620 回答
-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.

否则数字不会接近随机。

于 2019-06-13T18:16:00.507 回答
-3

我刚刚在互联网上找到了这个。这应该有效:

DWORD random = ((min) + rand()/(RAND_MAX + 1.0) * ((max) - (min) + 1));
于 2008-11-13T23:56:53.060 回答