2017-07-02 3 views
2

Ich habe den folgenden Code (the xorshift128+ code from Wikipedia Vektortypen zu verwenden, modifiziert):Mein vektorisiert Xorshift + ist nicht sehr zufällig

#include <immintrin.h> 
#include <climits> 

__v8si rand_si() { 
    static auto s0 = __v4du{4, 8, 15, 16}, 
     s1 = __v4du{23, 34, 42, 69}; 
    auto x = s0, y = s1; 
    s0 = y; 
    x ^= x << 23; 
    s1 = x^y^(x >> 17)^(y >> 26); 
    return (__v8si)(s1 + y); 
} 

#include <iostream> 
#include <iomanip> 
void foo() { 
    //Shuffle a bit. The result is much worse without this. 
    rand_si(); rand_si(); rand_si(); rand_si(); 
    auto val = rand_si(); 

    for (auto it = reinterpret_cast<int*>(&val); 
     it != reinterpret_cast<int*>(&val + 1); 
     ++it) 
     std::cout << std::hex << std::setfill('0') << std::setw(8) << *it << ' '; 
    std::cout << '\n'; 
} 

die Ausgänge

09e2a657 000b8020 1504cc3b 00110040 1360ff2b 00150078 2a9998b7 00228080 

Jede andere Zahl ist sehr klein, und keine haben das führende Bit gesetzt. Auf der anderen Seite, mit Xorshift * statt:

__v8si rand_si() { 
    static auto x = __v4du{4, 8, 15, 16}; 
    x ^= x >> 12; 
    x ^= x << 25; 
    x ^= x >> 27; 
    return x * (__v4du)_mm256_set1_epi64x(0x2545F4914F6CDD1D); 
} 

bekomme ich die viel bessere Leistung

0889632e a938b990 1e8b2f79 832e26bd 11280868 2a22d676 275ca4b8 10954ef9 

Aber nach Wikipedia, ist Xorshift + ein guter PRNG und erzeugt eine bessere pseudo-Zufälligkeit als Xorshift * . Habe ich einen Fehler in meinem RNG-Code, oder verwende ich ihn falsch?

+2

Sie bauen mit 'clang', oder? [gcc sagt] (https://godbolt.org/g/qLUFuz) * : 11: 17: note: -flax-vector-conversions, um Umwandlungen zwischen Vektoren mit unterschiedlichen Elementtypen oder Zahlen von Unterteilen * für 'return s1 zu erlauben + y; ' –

+0

@PeterCordes, Ja, das ist richtig. – Dan

+0

Warum verwenden Sie '__v8si' und' __v4du' anstelle der standardmäßigen von Intel definierten Typen? –

Antwort

3

Ich denke, dass Sie einen Zufallsgenerator nicht beurteilen sollten, indem Sie 8 Zahlen betrachten, die er erzeugte. Darüber hinaus benötigen Generatoren in der Regel eine gute Seeding (Ihre Seeding kann als schlecht angesehen werden - Ihre Seeds beginnen mit fast allen Bits Nullen. Calling rand_si() nur ein paar Mal ist nicht genug für die Bits zu "verbreiten").

Also ich empfehle Ihnen, richtige Seeding zu verwenden (zum Beispiel ist eine einfache Lösung rand_si() noch öfter anrufen).

xorshift* sieht aus wie besser durch die endgültige Multiplikation, so hat es nicht leicht Fehlverhalten wegen unzureichender Aussaat entdeckt.

Ein Tipp: Vergleichen Sie die Zahlen, die Ihr Code mit der ursprünglichen Implementierung generiert. Auf diese Weise können Sie sicher sein, dass Ihre Implementierung korrekt ist.

2

geza's Antwort war genau richtig, die Aussaat war der Schuldige. Es funktionierte viel besser, um es mit einem Standard-64-Bit-PRNG zu setzen:

void seed(uint64_t s) { 
    std::mt19937_64 e(s); 
    s0 = __v4du{e(), e(), e(), e()}; 
    s1 = __v4du{e(), e(), e(), e()}; 
} 
+0

Hier nur ein paar Beispiele: Die Reihenfolge der 'e()' -Aufrufe ist in s0 und s1 nicht spezifiziert (der Compiler kann die Reihenfolge der 'e()' -Aufrufe für den Konstruktor frei bestimmen). Wenn Sie also ein reproduzierbares Ergebnis zwischen verschiedenen Compiler-/Compiler-Versionen benötigen, müssen Sie zuerst die Ergebnisse von 'e()' in Variablen einfügen. – geza

+1

@geza Ist die Reihenfolge [in einer Klammer-Init-Liste definiert] (https://stackoverflow.com/q/14060264/603688) nicht? – Dan

+0

oops, du hast absolut recht!Es tut mir leid für die schlechten Informationen. Der Standard macht eine Ausnahme für diesen Fall. Interessant... – geza

Verwandte Themen