2016-05-23 6 views
4

Ich sehe ein seltsames Verhalten mit der C++ 11 std::uniform_real_distribution Kompilierung mit Apple LLVM Version 7.0.2 (clang-700.1.81). Durch Aufruf von operator() werden Ergebnisse außerhalb des Verteilungsbereichs gerendert. Das minimale Beispielprogramm reproduziert unter der MüheOS X libC++ std :: uniform_real_distribution Bug

// Example program 
#include <random> 
#include <iostream> 
#include <string> 

template< int power > 
constexpr uint64_t power_of_two(){ 
    return 2 * power_of_two< power - 1 >(); 
} 

template< > 
constexpr uint64_t power_of_two<0>(){ 
    return 1; 
} 

std::linear_congruential_engine 
< uint64_t, 273673163155, 13, power_of_two<48>() > 
rng; 

std::uniform_real_distribution<double> angle_cosine(-1, 1); 

int main() 
{ 
    std::cout << angle_cosine.a() << " " << angle_cosine.b() << '\n' << std::endl; 

    for (int i = 0; i < 4; ++i){ 
     std::cout << angle_cosine(rng) << std::endl; 
    } 
} 

Kompilieren und Ausführen online (vermutlich mit g ++) macht vernünftige Ergebnisse

-1 1 

-0.529254 
-0.599452 
0.513316 
-0.604338 

jedoch Kompilieren und lokal macht unvernünftig Ergebnisse ausgeführt wird.

-1 1 

130349 
37439.4 
42270.5 
45335.4 

Habe ich etwas übersehen oder habe ich einen Fehler in libC++ festgestellt? Wenn Letzteres der Fall ist, ist sich jemand einer Arbeit bewusst?

+0

Scheint wie ein Fehler für mich. Es hat mit der Wahl der Werte für "a", "c" und "m" zu tun. Es funktioniert gut für die meisten populären Wahlen, die [hier] aufgelistet sind (https://en.wikipedia.org/wiki/Linear_congruential_generator), aber scheitert für jene zwei Einstellungen, die 'm = 2^48' –

Antwort

8

Es ist ein Fehler im linearen Kongruenzgenerator LLVM und nicht in der gleichmäßigen realen Verteilung. Die gleichmäßige reale Verteilung setzt voraus, dass die vom Generator zurückgegebene Zahl zwischen den Min- und Max-Werten des Generators (einschließlich) liegt. Das ist eine Voraussetzung für jeden Generator. Der LLVM-Generator für lineare Kongruenz mit dieser Zahlengruppe erfüllt diese Anforderung nicht.

Der lineare LLVM-Kongruenzgenerator verwendet einen alten Algorithmus, um einen Überlauf zu vermeiden, den Schrage-Algorithmus anstelle von (a*x+c)%m für Ihre Zahlengruppe. Die LLVM-Implementierung dieses Algorithmus garantiert praktisch, dass Ihr Satz a, c, m Zahlen größer als m generiert.

Sie können den LLVM-Code hier sehen: https://llvm.org/svn/llvm-project/libcxx/trunk/include/random. Suche nach 'Schrages Algorithmus'. Ihre Wahl von a, c und m rufen Sie das erste der vier Vorkommen dieses Begriffs auf.

Übrigens, es gibt auch einen Fehler in Ihrem Code. Diese magischen Zahlen 273673163155 und 13 sollen Basis 8 sein. Das sind die Nummern, die von drand48 verwendet werden. Das zufällige Auswählen von Werten für a, c und m führt fast zwangsläufig zu einem fehlerhaften Zufallszahlengenerator.

Ich empfehle Wechsel zu std::mt19937 oder std::mt19937_64.

+1

Beeindruckende Antwort! – trojanfoe

+0

Ich konnte in der LLVM Bugzilla-Datenbank nichts über std :: linear_congriental_generator (oder sogar "congruential") finden, also habe ich einen Fehlerbericht eingereicht. –

+0

Erstens, danke für die umfassende Antwort. Zweitens, danke, dass Sie den Fehlerbericht eingereicht haben. Ich bin sehr beeindruckt, dass Sie meinen fummeligen Versuch, drand48 zu implementieren, erkannt haben und sogar mein Versehen bemerkt haben, von Oktal zu Dezimal auf den drand48-Konstanten zu konvertieren. – apmccartney