生成随机布尔值

时间:2017-04-10 17:24:29

标签: c++ random boolean

我目前正在用C ++实现Eller's Algorithm,一个小细节让我对迷宫的随机性感到困扰。

到目前为止,我使用以下代码生成随机bool

bool randomBool()
{
    return 0 + (rand() % (1 - 0 + 1)) == 1;
}

// In main.cpp

time_t seconds;
time(&seconds);
srand((unsigned int) seconds);

但经过调试后,我经常会看到重复生成truefalse,有时最多连续生成30次。

这个算法是真正随机的还是C ++中有更好的方法?

5 个答案:

答案 0 :(得分:10)

C ++ 11中的STL构建了优于rand()的随机数生成方法。您可以通过0或1的随机整数模拟随机布尔值:

#include <iostream>
#include <random>

int main(int argc, char *argv[]) {
    auto gen = std::bind(std::uniform_int_distribution<>(0,1),std::default_random_engine());
    const unsigned int N = 100;
    unsigned int numTrue = 0;
    unsigned int numFalse = 0;
    for (int i = 0; i < 100; ++i) {
        bool b = gen();
        if (b) ++ numTrue;
        else ++numFalse;
    }
    std::cout << numTrue << " TRUE, " << numFalse << " FALSE" << std::endl;
}

您可以在标准C ++参考中找到有关此库的更多详细信息。例如,如果你想要的是50/50比例的&#34;真&#34;和&#34;假&#34;值,您可以创建一个介于0和1之间的随机浮点数,并调用小于某个阈值的值z为true,否则为false。

为什么你会看到长条纹,我认为

我还没有解决为什么你会得到30个真正的&#34;真实的&#34;或&#34;假&#34;与您的代码连续。虽然不应再使用rand(),并且您的代码中似乎有一些不必要的加法和减法,但不应该出现这样的问题。但是,我现在意识到你问题中的文字含糊不清。如果您连续30次运行并退出程序,您应该会看到重复的值 - 即使使用我的代码。大多数随机数生成器实际上是伪随机数生成器。每次运行程序时,它们都会生成相同的随机数序列;这对于结果的一致性很重要。但是,当程序运行时(例如将randomBool()置于循环中),您不应该看到这么长的条纹,因为它们非常不可能。

长条纹不稳定

我很惊讶收到的评论意见不同意我的断言,即30&#34;真实&#34;或&#34;假&#34;随机布尔是不可能的(当真或假同样可能时)。我意识到对概率的一个常见误解是&#34;运气&#34;试图解决问题,所以如果连续几次投掷硬币,那么宇宙将尝试纠正这一点并使尾部更容易发生。由于这种误解,人们低估了所有头部和所有尾巴的条纹可能性,我认为对这个答案和主要问题的评论的动机是纠正这个常见的错误。

然而,有一个真正的原因,长条纹(特别是长达30条)越来越不可能。使用随机无偏硬币抛出的语言,每个IID(独立且相同分布的)抛硬币只有50%的机会与之前相同。因此,长条纹的概率随着条纹的长度呈指数下降。对于长度为L的条纹,所有头部条纹的概率为1 ^ 2 ^ L;任何一种条纹的概率是2 ^ 2 L或1 in 2 ^(L-1)。以下是一些演示代码:

#include <iostream>
#include <random>
#include <map>

bool randomBool() {
    static auto gen = std::bind(std::uniform_int_distribution<>(0,1),std::default_random_engine());
    return gen();
}

int main(int argc, char *argv[]) {

    const unsigned int N = 1e8;
    std::map<unsigned int,unsigned int> histogram;
    bool current = randomBool();
    unsigned int currentLength = 1;
    for (int i = 0; i < N; ++i) {
        bool b = randomBool();
        if (b == current) {
            ++currentLength;
        } else {
            auto it = histogram.find(currentLength);
            if (it != histogram.end())
                it->second += 1;
            else
                histogram.insert(std::make_pair(currentLength,1));
            currentLength = 1;
        }
        current = b;
    }

    for (auto pair : histogram) 
        std::cout << "STREAK LENGTH " << pair.first << " OCCURS " << pair.second << " TIMES" << std::endl;
}

输出直方图是:

STREAK LENGTH 1 OCCURS 25011106 TIMES
STREAK LENGTH 2 OCCURS 12503578 TIMES
STREAK LENGTH 3 OCCURS 6249056 TIMES
STREAK LENGTH 4 OCCURS 3125508 TIMES
STREAK LENGTH 5 OCCURS 1560812 TIMES
STREAK LENGTH 6 OCCURS 781206 TIMES
STREAK LENGTH 7 OCCURS 390143 TIMES
STREAK LENGTH 8 OCCURS 194748 TIMES
STREAK LENGTH 9 OCCURS 97816 TIMES
STREAK LENGTH 10 OCCURS 48685 TIMES
STREAK LENGTH 11 OCCURS 24327 TIMES
STREAK LENGTH 12 OCCURS 12176 TIMES
STREAK LENGTH 13 OCCURS 6149 TIMES
STREAK LENGTH 14 OCCURS 3028 TIMES
STREAK LENGTH 15 OCCURS 1489 TIMES
STREAK LENGTH 16 OCCURS 811 TIMES
STREAK LENGTH 17 OCCURS 383 TIMES
STREAK LENGTH 18 OCCURS 193 TIMES
STREAK LENGTH 19 OCCURS 104 TIMES
STREAK LENGTH 20 OCCURS 43 TIMES
STREAK LENGTH 21 OCCURS 20 TIMES
STREAK LENGTH 22 OCCURS 14 TIMES
STREAK LENGTH 23 OCCURS 4 TIMES
STREAK LENGTH 24 OCCURS 3 TIMES

难以计算多个翻转数N中的长度L的预期条纹数,因为存在许多长度为L的重叠区段,其中可能存在这样的条纹。但请注意,此直方图遵循大致指数分布,每个条目大约是前一个条目的一半。

最大连胜数为24 [注意:之前版本中的错误计为23]。在任何24个投掷的独立字符串中,该长度连续的概率是1 ^ 2 ^(24-1),或者约为800万。由于在1e8投掷中大约有1e8 / 24~430万个这样的单独延伸,我们期待少量这样的条纹,所以这似乎是正确的[我的上述警告,计算确切的期望是困难的]。同时,在30次翻转的任何独立延伸中,长度为30的条纹在5.37亿的概率为1,并且甚至比长度为24的条纹更不可能。

答案 1 :(得分:0)

如果rand()是真正的伪随机数,它是真正的伪随机数,但如果RAND_MAX是偶数,则分布可能非常不均匀(即,偶数的数量多于奇数的数量)。但通常RAND_MAX足够大,差异可以忽略不计。

答案 2 :(得分:0)

bool randomBool() {
    return 0 + (rand() % (1 - 0 + 1)) == 1;
}

这可能是将rand()的输出转换为布尔值的最差方法。在许多实现中,低阶位比高阶位的随机性要小得多。

理想情况下,您完全使用其他内容,但如果必须使用rand(),请尝试:

bool randomBool() {
   return rand() > (RAND_MAX / 2);
}

答案 3 :(得分:0)

伪随机数发生器的低阶位倾向于提供较少的随机性。对于内置rand()函数尤其如此,该函数通常实现为LCG。生成随机bool的最佳方法是使用MSB位。这实际上是标准Bernoulli distribution,概率为1/2

#include <cmath>
#include <cstdlib>

inline bool random_bool()
{
   static const int shift = static_cast<int>(std::log2(RAND_MAX));
   return (rand() >> shift) & 1;
}

答案 4 :(得分:0)

这是一个C ++ 11函数模板,该模板以指定的概率生成布尔结果(二项式分布)(默认为0.5表示均匀):

#include <random>
template <typename Prob>
bool binomial_trial(const Prob p = 0.5) {
    static auto dev = std::random_device();
    static auto gen = std::mt19937{dev()};
    static auto dist = std::uniform_real_distribution<Prob>(0,1);
    return (dist(gen) < p);
}
相关问题