it-swarm.com.de

Zufälliger Boolescher Wert wird generiert

Ich implementiere gerade Ellers Algorithmus in C++ und ein kleines Detail stört mich an der Zufälligkeit des Labyrinths.

Bisher habe ich den folgenden Code verwendet, um eine zufällige bool zu generieren:

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

// In main.cpp

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

Beim Debuggen werden jedoch häufig wiederholte true oder false generiert, manchmal bis zu 30 Mal hintereinander.

Ist dieser Algorithmus wirklich zufällig oder gibt es eine bessere Methode in C++?

7

Die STL in C++ 11 enthält Methoden zur Erzeugung von Zufallszahlen, die Rand() überlegen sind. Sie können einen zufälligen Booleschen Wert durch eine zufällige Ganzzahl von 0 oder 1 simulieren:

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

Weitere Details zu dieser Bibliothek finden Sie in den Standard-C++ - Referenzen. Wenn Sie beispielsweise ein anderes Verhältnis von "true" und "false" als 50/50 wünschen, können Sie eine zufällige Gleitkommazahl zwischen 0 und 1 erstellen und Werte aufrufen, die unter einem Schwellenwert von z true liegen, andernfalls false.

Warum Sie lange Streifen sehen, denke ich

Ich habe nicht angesprochen, warum Sie mit Ihrem Code 30 Werte von "true" oder "false" in einer Reihe erhalten. Obwohl Rand() nicht mehr verwendet werden sollte und Sie scheinbar unnötige Additionen und Subtraktionen von Einsen und Nullen in Ihrem Code haben, sollte es kein solches Problem geben. Jetzt ist mir jedoch klar, dass der Text in Ihrer Frage nicht eindeutig ist. Wenn Sie Ihr Programm 30 Mal hintereinander ausführen und beenden, sollten Sie mit wiederholten Werten rechnen - auch mit meinem Code. Die meisten Zufallszahlengeneratoren sind wirklich Pseudozufallszahlengeneratoren. Jedes Mal, wenn Sie das Programm ausführen, wird die Sequenz same von Zufallszahlen erzeugt. Dies ist wichtig für die Konsistenz der Ergebnisse. Während das Programm ausgeführt wird (z. B. wenn Sie Ihre randomBool() in eine Schleife setzen), sollten Sie jedoch keine Streifen mit einer solchen Länge sehen, da dies höchst unwahrscheinlich wäre.

Unwahrscheinlichkeit von langen Streifen

Ich war überrascht, Kommentare zu erhalten, die meiner Behauptung widersprachen, dass ein Streifen von 30 "wahren" oder "falschen" zufälligen Booleschen unwahrscheinlich ist (wenn wahr oder falsch gleich wahrscheinlich sind). Mir ist klar, dass ein weit verbreitetes Missverständnis der Wahrscheinlichkeit ist, dass „Glück“ versucht, Dinge zu glätten, und so, dass, wenn ein Münzwurf ein Heads paar Male in Folge gekommen ist, dann wird das Universum versuchen, dies zu korrigieren und macht einen Schwanz mehr wahrscheinlich. Aufgrund dieses Missverständnisses unterschätzen die Leute die Wahrscheinlichkeit, Streifen von Kopf und Schwanz zu bekommen, und ich denke, die Motivation der Kommentare zu dieser Antwort und der Hauptfrage bestand darin, diesen häufigen Fehler zu korrigieren.

Es gibt jedoch einen real Grund dafür, dass lange Streifen (insbesondere 30) zunehmend unwahrscheinlich sind. Unter Verwendung der Sprache der zufälligen, unverfälschten Münzwürfe hat jeder IID-Münzwurf (unabhängig und identisch verteilt) nur eine 50% ige Chance, mit dem vorherigen identisch zu sein. Somit nimmt die Wahrscheinlichkeit eines langen Streifens exponentiell mit der Länge des Streifens ab. Für einen Streifen der Länge L beträgt die Wahrscheinlichkeit eines Streifens aller Köpfe 1 zu 2 ^ L; Die Wahrscheinlichkeit eines Streifens ist 2 in 2 ^ L oder 1 in 2 ^ (L-1). Hier ist ein Code zum Demonstrieren:

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

Das Ausgabe-Histogramm lautet:

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

Es ist schwierig, die erwartete Anzahl von Streifen der Länge L in einer Anzahl von Flips N zu berechnen, da es viele überlappende Strecken der Länge L gibt, in denen ein solcher Streifen existieren könnte. Beachten Sie jedoch, dass dieses Histogramm einer ungefähr exponentiellen Verteilung folgt, wobei jeder Eintrag ungefähr die Hälfte des vorherigen Eintrags beträgt.

Die maximale Streifenlänge beträgt 24 [Anmerkung: Ein Fehler in der vorherigen Version hat dies als 23 gewertet]. Die Wahrscheinlichkeit für einen Streifen dieser Länge in einer unabhängigen Folge von 24 Würfen beträgt 1 zu 2 ^ (24-1) oder ungefähr 1 zu 8 Millionen. Da in 1e8 dort schleudert sind etwa 1E8/24 ~ 4,3 Mio. solche getrennte Strecken erwarten wir eine kleine Anzahl solcher Streifen, so scheint dies zu Recht [mit meinem obigen Vorbehalt, dass die genaue Erwartung Berechnung schwierig ist]. Ein Streifen mit der Länge 30 hat eine Wahrscheinlichkeit von 1 zu 537 Millionen in einem unabhängigen Streck von 30 Flips und ist viel weniger wahrscheinlich als ein Streifen mit der Länge 24.

9
jwimberley

Hier ist eine C++ 11-Funktionsvorlage, die boolesche Ergebnisse (Binomialverteilung) mit einer bestimmten Wahrscheinlichkeit generiert (Standard ist 0,5 für Uniform):

#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);
}
0
mariusm

Die Bits niedrigerer Ordnung von Pseudozufallszahlengeneratoren neigen dazu, weniger Zufälligkeit bereitzustellen. Dies gilt insbesondere für die integrierte Funktion Rand(), die in der Regel als LCG implementiert ist. Der beste Weg, eine zufällige bool zu generieren, ist die Verwendung des MSB-Bits. Dies ist eigentlich eine Standard Bernoulli-Distribution mit Wahrscheinlichkeit 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;
}
0
plasmacel

Es ist wirklich pseudozufällig, wenn Rand() wirklich pseudozufällig ist, obwohl die Verteilung sehr leicht ungleichmäßig sein kann, wenn Rand_MAX gerade ist (d. H. Es gibt eine gerade Zahl mehr als ungerade Zahlen). In der Regel ist Rand_MAX jedoch so groß, dass der Unterschied vernachlässigbar ist.

0
yuri kilochek
bool randomBool() {
    return 0 + (Rand() % (1 - 0 + 1)) == 1;
}

Dies ist vielleicht die schlechteste Möglichkeit, die Ausgabe von Rand() in einen Booleschen Wert umzuwandeln. Bei vielen Implementierungen sind die Bits niedrigerer Ordnung viel weniger zufällig als die Bits höherer Ordnung.

Idealerweise würden Sie etwas ganz anderes verwenden, aber wenn Sie Rand() verwenden müssen, versuchen Sie Folgendes:

bool randomBool() {
   return Rand() > (Rand_MAX / 2);
}
0
David Schwartz