Globale Variablen in statischen Bibliotheken



  • Ich frage mich halt einfach, ob man bei

    void Func()
    {
        Randomizer r;
        Use(r.Random());
    }
    

    nicht lieber

    void Func()
    {
        static Randomizer r;
        Use(r.Random());
    }
    

    einsetzen würde (von Threadsicherheit mal abgesehen)? Denn auch wenn man mehrere unabhängige Randomizers jeweils am Anfang verwürfelt, ist die aus verschiedenen Randomizer -Aufrufen resultierende Gesamtsequenz unschön. Es könnte ja sein, dass z.B. immer nur jede Sekunde so ein Aufruf stattfindet, und abhängig von der Qualität des Seeders eine unregelmässige Verteilung oder so auftritt.

    Man könnte zwar argumentieren, dass in so einem Fall ein einzelner Randomizer benutzt werden sollte (wie im Code mit static ), aber dann hat man eben wieder einen Vorteil weniger durch die unabhängigen Objekte.



  • @ hustbaer:
    Okay. Ja, mir scheint sowohl globale als auch unabhängige lokale RNGs haben ihre Vorteile. Vielleicht sollte ich so eine Klasse einrichten und davon eine globale Instanz erzeugen. Diese wird dann benutzt wenn nötig, und in den Fällen mit grösserem Kontext kann ich direkt neue Instanzen einsetzen.



  • Wie wär's mit

    void Foo::Func()
    {
       Use(m_rng.Get());
    }
    
    // oder
    template <class G>
    void Func(G& rng)
    {
       Use(rng.Get());
    }
    
    // oder
    void Func(AbstractRng& rng)
    {
       Use(rng.Get());
    }
    

    ?



  • Gut, vor allem das mit der Memberfunktion könnte ich wohl ab und zu einsetzen. Zum Beispiel könnten Partikel-Emitter, die eine zufällige Abweichung von der Ausstossrichtung haben, nun so einen Generator bekommen. Andererseits ist das eventuell Overkill, da das Resultat wahrscheinlich nicht gross anders aussieht als beim globalen Generator, gerade in Single-Thread-Programmen.



  • Naja, es muss ja nicht gleich ein MT sein (braucht viel Speicher für seinen Zustand) der über teure OS Funktionen (langsam) geseedet wird.

    Ein einfacher LCG wird's da vermutlich tun. Oder der Generator den volkard mal gepostet hat - mir fällt der Name gerade nicht ein (braucht IIRC bloss 2x 32 Bit int's als Zustand und produziert Sequenzen die für viele Anwendungen fast so gut oder besser sind, wie die aus einem MT).

    Und seeden kann man sowas auch trivial, z.B. mit currentFrameNumber ^ reinterpret_cast<size_t>(this) (this = neue Partikel-Emitter-Instanz) oder sowas.



  • Nexus schrieb:

    Gut, vor allem das mit der Memberfunktion könnte ich wohl ab und zu einsetzen. Zum Beispiel könnten Partikel-Emitter, die eine zufällige Abweichung von der Ausstossrichtung haben, nun so einen Generator bekommen. Andererseits ist das eventuell Overkill, da das Resultat wahrscheinlich nicht gross anders aussieht als beim globalen Generator, gerade in Single-Thread-Programmen.

    Leider zerschieße ich mir regelmäßig den globalen Zufallszahlengenerator mit:

    int raki(){
       do
          tuwas();
       while(rand()!=0);
    }
    

    Der Partikel-Emmitter bekommt jetzt nur noch die Zahlen der Zufallsfolge zu sehen, die eine 0 als Vorgänger hatten.
    (Nee, sowas programmiere ich nicht, aber es ist eine süße Vorstellung.)



  • Ich benutze TR1.Random (gefällt mir noch, abgesehen vom verbuggten variate_generator ). Ich habe bisher std::tr1::default_random_engine eingesetzt, was unter MSVC++ dem MT 19937 entspricht. Ist wohl für meine Anwendungen etwas Overkill. Es gibt ja bei Boost eine Tabelle mit Vergleichen, was habt ihr so für Erfahrungen gemacht? Ich denke, der Lineare Kongruenzgenerator hat im Allgemeinen ein akzeptables Verhältnis von Geschwindigkeit und Güte der Zufallszahlen... Oder würdet ihr was Anderes empfehlen?

    Verwendet ihr eigentlich andere Bibliotheken als TR1.Random bzw. Boost.Random?

    Hehe ja, rand() wird ohnehin in allen möglichen Bibliotheken benutzt. Wenn man Wert darauf legt, eine Zufallszahlenfolge für sich allein zu haben, ist das vielleicht nicht ganz das Wahre.



  • Nexus schrieb:

    Ich denke, der Lineare Kongruenzgenerator hat im Allgemeinen ein akzeptables Verhältnis von Geschwindigkeit und Güte der Zufallszahlen... Oder würdet ihr was Anderes empfehlen?

    Außer, es ist so egal, daß man einfach rand() nimmt, würde ich immer was anderes nehmen, denn es gibt viele, die sind auch schnell und haben keinen großen Zustand und sind viel besser.
    Ich mag den MultipyWithCarry am liebsten. Der Xorshift scheint viele Freunde zu haben.



  • Der Multiply With Carry sieht gut aus, er ist nicht zufällig unter einem anderen Namen im TR1? Denn deswegen eine externe Bibliothek benutzen möchte ich eigentlich nicht. Ist er einfach selbst implementiert, oder muss ich lange mit Parametern experimentieren und die Resultate testen? Sonst würde ich vorläufig eher einen fertigen aus TR1 nehmen, bis ich alles in Ruhe ausprobiert habe... 😉



  • Nexus schrieb:

    Der Multiply With Carry sieht gut aus, er ist nicht zufällig unter einem anderen Namen im TR1? Denn deswegen eine externe Bibliothek benutzen möchte ich eigentlich nicht. Ist er einfach selbst implementiert, oder muss ich lange mit Parametern experimentieren und die Resultate testen? Sonst würde ich vorläufig eher einen fertigen aus TR1 nehmen, bis ich alles in Ruhe ausprobiert habe... 😉

    //Multiply-With-Carry Pseudo Random Number Generator
    class Random {
    private:
        static UInt64 const a=4294967118;
        UInt64 x;
    public:
        Random(UInt64 seed=0) {
            x=seed+!seed;
        }
        UInt32 operator()() {
            x=a*(x&0xffffffff)+(x>>32);
            return x;
        }
    };
    
    //Numerical Receipes sagt:
    // Wenn der Seed in 1 <= x <= 2^32-1 liegt
    // und sowohl a*b-1 als auch (a*b-2)/2 Primzahlen sind,
    // dann ist die Periodenlänge gleich (a*b-2)/2.
    //Volkard sagt:
    // Die größte Zahl, die diese Bedingungen erfüllt, ist
    // obiges a=2^32-178 mit beinahe 2^63 als Periodenlänge. 	
    //Mit 1967773755 habe ich den Generator durch die die hard 
    // Tests geschickt und er war großartig! Obiges a habe ich 
    // noch nicht durchgeschickt.
    


  • Cool, vielen Dank 🙂

    Ich wusste nicht, dass das mit so wenig Code geht. Vielleicht schaue ich mir sogar ein DieHard-Testframework an und experimentier ein wenig damit herum. Hast du was dagegen, wenn ich den Code für eine Open-Source-Bibliothek nehme? Wenn du möchtest, kann ich dich auch gerne erwähnen.

    Und dann müsste ich noch schauen, ob ich das mit den 64-Bit einigermassen portabel hinkriege. Ansonsten könnte man das wohl auch mit 32 Bit lösen, evtl. unter In-Kauf-Nahme schlechterer Zufallseigenschaften. Mal schauen... 😉



  • Nexus schrieb:

    Hast du was dagegen, wenn ich den Code für eine Open-Source-Bibliothek nehme?

    Nö, mach nur.

    Nexus schrieb:

    Wenn du möchtest, kann ich dich auch gerne erwähnen.

    Wäre nett.



  • Nexus schrieb:

    Ansonsten könnte man das wohl auch mit 32 Bit lösen, evtl. unter In-Kauf-Nahme schlechterer Zufallseigenschaften.

    Die oberen Bits liegen leicht schief. Ich denke, das ist um so schlimmer, je kleiner a ist. Deswegen gebe ich nur die unteren 32 Bit raus.

    "A theoretical problem with MWC generators, pointed out by Couture and l'Ecuyer (1997) is that the most significant bits are slightly biased; "
    http://en.wikipedia.org/wiki/Multiply-with-carry_(random_number_generator)



  • Nexus schrieb:

    Vielleicht schaue ich mir sogar ein DieHard-Testframework an und experimentier ein wenig damit herum.

    Ich hab ein Paket namens dieharder gefunden. Scheint sehr leicht zu benutzen zu sein.

    ./a.out | dieharder -g 200 -a | tee mwc.txt
    
    int main(){
            UInt32 x[1024];
            Random rand;
            for(;;){
                    for(UInt32 i=0;i!=1024;++i)
                            x[i]=rand();//deswegen
                    cout.write((char*)&x,sizeof(x));
            }
    }
    

    Deswegen habe ich mich auch für op() als Schnittstelle entschieden, damit ich unten wie gewohnt rand() benutzen kann.

    Mal schauen, welchen Code der Compiler draus macht...

    movl    %ebx, %eax
            movl    %esi, %ebx
            mull    %edi
            xorl    %esi, %esi
            addl    %eax, %ebx
            adcl    %edx, %esi
            movl    %ebx, 32(%esp,%ecx,4)
    

    Aha, er nimmt die normale Multiplikation, die auf x86-Prozessoren (32 Bit) verfügbar ist. Ok, muß er auch, das ist kein 64-Bit-Rechner. Und er hat bemerkt hat, daß a eigentlich ein UIn32 ist. Das ist ja lieb von ihm.

    Der alte std::rand (mit buf->rand_type == TYPE_0 und ohne Threadsicherheit)
    http://sourceware.org/git/?p=glibc.git;a=blob;f=stdlib/random_r.c;h=51a2e8c812aee78783bd6d38c1b6269d41c8e47e;hb=refs/heads/release/2.13/master

    imull   $1103515245, %eax, %eax
            addl    $12345, %eax
            andl    $2147483647, %eax
            movl    %eax, (%ebx,%edx,4)
    


  • Hey, vielen Dank für deine Bemühungen! Dann werde ich es mit 64-Bit versuchen und das dieharder ausprobieren.

    Ich habe gestern mit dem hier herumprobiert und die erzeugten 32-Bit-Zufallszahlen binär in eine Datei geschrieben. Sowohl bei deinem als auch bei den TR1-Generatoren ist immer ein "pvalue" von 0 herausgekommen, wahrscheinlich hab ich was Grundlegendes falsch gemacht...


Anmelden zum Antworten