Würfeln



  • volkard schrieb:

    willste messergebnisgenauigkeit verzehnfachen, mußte leider 100mal mehr versuche machen.

    Die Nacht ist noch jung.

    🤡



  • Erhard Henkes schrieb:

    Die Differenzen bei der Gleichverteilung liegen jetzt im Promillebereich. Damit sollte man leben können. Wie hoch sind die Abweichungen eigentlich bei mechanischen Würfeln, auch ca. 0,1% ?

    sorry. das da hat mich schwer verwirrt. ich war mich sicher, daß du sachen wie die ungleichung von tschebyscheff und das gesetz der großen zahl kennst.
    also so mathe-sachen, die sagen, daß die relativen wahrscheinlichkeiten mit steigender anzahl der versuche sich langsam angleichen. und daher hab ich deine messung von 0.1% fehler für den lektronischen würfel dahingehend interpretiert, daß dein würfel ne echte schieflage hat.



  • @Marc++us: Deine Argumente stechen, ich werde Vorschlag 2 daher als Alternative in Betracht ziehen. Zunächst möchte ich aber Volkard's Einwände kapieren.

    @Volkard: Doch ich möchte verstehen, was Du mir mitzuteilen versuchst, vielleicht habe ich das Entscheidende noch nicht erfasst. Bezüglich der Bevorzugung der Zahlen 0 und 1 bei einem 6er-Würfel (in meinem Sourcecode übrigens 1 und 2, aber das ist nebensächlich) habe ich doch den Vorschlag von Marc++us übernommen und das größte Vielfache der höchsten Augenzahl unterhalb RAND_MAX minus 1 als Obergrenze für die erzeugten Zahlen vorgegeben. Dabei bin ich davon ausgegangen, dass die Gleichverteilung damit gewährleistet ist. Marc++us hat meiner Umsetzung, wenn ich das richtig verstanden habe, zugestimmt. Könnte aber noch ein Fehler drinnen stecken. Mir ist bisher aber noch keine systematische Verzerrung aufgefallen ( bei Einsatz von Dev-C++ 4.9.8.2, vielleicht gibt es da Unterschiede bezüglich rand() bei anderen Compilern ).

    /EDIT: scheint ein Missverständnis gewesen zu sein, dennoch die Frage:
    ist die nachstehende Variante günstiger? Ich denke nein.

    Man kann auch auf

    dRand = rand(); 
    dRand /= RAND_MAX; 
    dRand = dRand * 6 + 1; 
    nWuerfel = (int) dRand;
    

    umstellen, wenn das homogenere Ergenisse liefert. Ich werde es ausprobieren, da Dir das besser gefallen hat.



  • und daher hab ich deine messung von 0.1% fehler für den lektronischen würfel dahingehend interpretiert, daß dein würfel ne echte schieflage hat.

    Mir gefällt diese Abweichung nicht, ich würde sie gerne verringern. Liegt das an rand() oder an meinem Sourcecode? Wenn ich den Binärwürfel einsetze, ist die Abweichung deutlich geringer, aber das liegt daran, dass mehr Random-Zahlen zu einer Zahl zugeordnet werden.



  • Erhard Henkes schrieb:

    Man kann auch auf

    dRand = rand(); 
    dRand /= RAND_MAX;        // (1)
    dRand = dRand * 6 + 1;    // (2a)
    nWuerfel = (int) dRand;   // (2b)
    

    umstellen, wenn das homogenere Ergenisse liefert. Ich werde es ausprobieren, da Dir das besser gefallen hat.

    Kann der Wechsel der Zahlendarstellung etwas daran ändern, daß es 32768 verschiedene Werte gibt? Es gibt ab der Stelle (1) eben 32768 unterschiedliche double-Werte statt int-Werte. Haben wir was geändert?

    Ändert die Skalierung in den Schritten (2a) und (2b) etwas daran, daß 32768 KEIN ganzzahliges Vielfaches von 6 ist?

    Also was bringt der obige Rechentrick für die Häufigkeit?

    Btw, Du kannst Deine Implementation mit dem Testgenerator doch prüfen. Wenn Du mit dem vorgestellten Testgenerator richtige Gleichverteilung erhälst, liegt es ausschließlich an rand().[aber wie gesagt: Du mußt dann genau n * 6 * 32768 mal ziehen]



  • Mit dem Testgenerator läuft das spitzenmäßig.
    30.000.000 Versuche ergeben jeweils 5.000.000 einer Zahl bei 6 Möglichkeiten.
    Zur Kontrolle:

    unsigned int wuerfelt_mit_externem_Generator() 
      { 
        unsigned int r; 
        do{ r = zahlengenerator_.getNum(); }  
          while ( r >= maxrandom_ ); 
        return ( r % maxzahl_ +1 );  
      }
    

    Gegenprobe:
    1: 5.000.763
    2-6: 4.999.847 bzw. 4.999.848

    unsigned int wuerfelt_mit_externem_Generator() 
      { 
        unsigned int r; 
        do{ r = zahlengenerator_.getNum(); }  
          while ( false /*r >= maxrandom_*/ ); 
        return ( r % maxzahl_ +1 );  
      }
    

    Deine Vorgehensweise funktioniert also prächtig. q.e.d. 🙂
    Dann wird dieser Teil mit dem Modulo und dem Abschneiden der oberen Werte jetzt abgehakt.

    Übrigens:
    "Du mußt dann genau n * 6 * 32768 mal ziehen"
    30.000.000 Versuche ergibt da keine natürliche Zahl. Dennoch geht es perfekt.

    Ansonsten zum Konzept: "Do The Simplest Thing That Could Possibly Work" (XP-Spruch) http://xp.c2.com/DoTheSimplestThingThatCouldPossiblyWork.html 😃

    @Volkard: gut, nachdem dies geklärt ist, wie funktioniert der "mersenne prime twin generator" zum Erzeugen von Zufallszahlen, oder genauer: wo finde ich den C++-Code zum Einbinden in meinen Würfel?



  • Erhard Henkes schrieb:

    ist die nachstehende Variante günstiger? Ich denke nein.
    Man kann auch auf

    dRand = rand(); 
    dRand /= RAND_MAX; 
    dRand = dRand * 6 + 1; 
    nWuerfel = (int) dRand;
    

    umstellen, wenn das homogenere Ergenisse liefert. Ich werde es ausprobieren, da Dir das besser gefallen hat.

    nee, die gefällt mir gar nicht gut. dem rechnen mit fließkommazahlen traue ich nicht.
    sollte nicht bei jedem compiler ne funktion MulDiv dabei sein, die zwei 32-bitter plutimiziert und das ergebnis durch nen weiteren dividiert?



  • volkard schrieb:

    Erhard Henkes schrieb:

    @Volkard: gut, nachdem dies geklärt ist, wie funktioniert der "mersenne prime twin generator" zum Erzeugen von Zufallszahlen, oder genauer: wo finde ich den C++-Code zum Einbinden in meinen Würfel?

    oh, konnteste nicht finden. heißt twister und nicht twin. den satz "Four times faster than rand()" kannste auch vergessen. nicht auf normalen prozessoren. aber vorzüge hat er ja trotzdem noch. ne saulange periode.

    kein c++-code.
    aber krasses c!
    http://www.math.keio.ac.jp/home2/matumoto/public_html/mt19937int.c
    kriegst bestimmt viel mecker, wenn du nicht die #defines wegmachst und so.



  • @Volkard:
    Danke für den Link.

    @all:
    Ich habe das Konzept 2 von Marc++us bezüglich der Template-Würfel-Klasse mal probeweise umgesetzt. Umschaltung zur Laufzeit geht jetzt nicht mehr (Nachteil). Diese Lösung gefällt mir nun dennoch besser, da die Aufgaben "(Zufalls)Zahlen generieren und ausgeben" und "Verteilung glätten / Umsetzen auf maximale Augenzahl / Augenzahl ausgeben" auf zwei Klassen verteilt wurden. Damit ist der Würfel nicht mehr fest an rand() gekettet. Das ist der Hauptvorteil. Die Klasse zur Erzeugung von Zufallszahlen mit rand() besteht jetzt nur aus einer Funktion. Ich habe bewusst seed_flag nicht zum Attribut (war das ein Vorschlag von Volkard?) gemacht, da Marc++us und Shade vorgeschlagen haben, dieses Flag lokal in der Funktion, die es braucht, zu setzen.

    So jetzt könnt ihr den Entwurf wieder zerfetzen 😃 :

    #include <iostream> 
    #include <iomanip>  
    #include <conio.h>  
    #include <cstdlib> 
    #include <ctime>  
    
    class RandomStdlib // verwendet rand()
    { 
    public: 
       int getNum() const
       { 
         static bool seed_flag=0;
         if(!seed_flag)
         { 
           srand( static_cast<unsigned>(time(0)) );  
           seed_flag = true; 
         } 
         return rand();
       } 
    };
    
    class RandomTestEqual // Test auf Gleichverteilung
    { 
    private: 
       int num_; 
    public: 
       RandomTestEqual() : num_(RAND_MAX - 1){}; 
       int getNum()  
       { 
          num_++; 
          if (num_ >= RAND_MAX) num_ = 0; 
          return num_; 
       } 
    };
    
    template<typename T_Generator> class Wuerfel  
    {  
    private:
      const unsigned int maxzahl_; 
      const unsigned int maxrandom_; 
      T_Generator zahlengenerator_; // Template-Typ
    
    public:  
      Wuerfel(unsigned int maxzahl):maxzahl_(maxzahl),maxrandom_(RAND_MAX-(RAND_MAX%maxzahl)) {}  
    
      unsigned int wuerfelt() 
      { 
        unsigned int r; 
        do{ r = zahlengenerator_.getNum(); }  
          while ( r >= maxrandom_ ); 
        return ( r % maxzahl_ +1 );  
      }      
    };  
    
    int main()  
    {  
      const unsigned long long Serie     = 3;  
      const unsigned long long Versuche  = 30000000;  
      const unsigned int limit           = 200;  
      const unsigned int moeglichkeiten  = 6;  
    
      Wuerfel<RandomTestEqual>  w0a(moeglichkeiten);  
      Wuerfel<RandomTestEqual>  w0b(2);  
      Wuerfel<RandomStdlib>     w1a(moeglichkeiten);  
      Wuerfel<RandomStdlib>     w1b(2);  
    
      unsigned long long H[moeglichkeiten+1];  
    
      for(unsigned long long i=1; i<Serie+1; ++i)  
      {  
        for(unsigned int j=0; j<moeglichkeiten+1; ++j) H[j] = 0;  
    
        for(unsigned long long k=1; k<Versuche+1; ++k)  
        {  
          unsigned int wurf = w1a.wuerfelt(); // hier wird gewürfelt 
    
          if(Versuche<limit) std::cout << wurf << " ";  
          ++H[wurf];  
        }  
    
        for(unsigned int c=1; c<moeglichkeiten+1; ++c)  
        {  
          std::cout << std::endl << c << ": " << H[c] << " " << std::setprecision(7)   
                    << 100 * static_cast<float>(H[c]) / Versuche << " %";  
          H[0] += H[c];  
        }  
        std::cout << std::endl << "Wuerfe insgesamt: " << H[0] << std::endl << std::endl;  
      }  
      getch();  
    }
    


  • Erhard Henkes schrieb:

    Ich habe bewusst seed_flag nicht zum Attribut (war das ein Vorschlag von Volkard?) gemacht, da Marc++us und Shade vorgeschlagen haben, dieses Flag lokal in der Funktion, die es braucht, zu setzen.

    habe nir gewollt, ein seed_flag als attribut zu nehmen.
    will aber weiterhin, daß der seed attribut ist!
    und ich bin sicher, marc++us und shade unterstützen das auch.



  • Die Funktion srand(...) hat folgende Signatur: void srand( unsigned int seed );

    seed ist hier "static_cast<unsigned>(time(0))"

    Diesen Wert vom Typ unsigned int würdest Du also als Attribut der Klasse festlegen? Habe ich das so richtig verstanden:

    class RandomStdlib // verwendet rand()
    { 
    private: 
       const unsigned int seed_;
    public: 
       RandomStdlib():seed_( static_cast<unsigned>(time(0)) ){}
    
       int getNum() const
       { 
         static bool seed_flag=0;
         if(!seed_flag)
         { 
           srand(seed_);  
           seed_flag = true; 
           std::cout << "Seed fuer rand(): " << seed_ << std::endl << std::endl;
         } 
         return rand();
       } 
    };
    

    Wenn ja, warum bitte? Was bringt mir das?
    Wenn nein, bitte genauere Erklärung.



  • zeig mal die implemetierung der rand().



  • In der einen Klasse ist getNum() konstant, in der anderen nicht. Das macht doch nichts aus, soweit ich weiß, oder doch?



  • Volkard: "zeig mal die implemetierung der rand()."

    Kann ich leider nicht, ist rand() aus der Standardbibliothek <cstdlib>.
    😕



  • 😉

    Im Verzeichnis (z.B.) D:\Programme\Microsoft Visual Studio .NET\Vc7\crt\src findest Du eine Datei mit dem Namen "rand.c":

    /***
    *rand.c - random number generator
    *
    *       Copyright (c) 1985-2001, Microsoft Corporation. All rights reserved.
    *
    *Purpose:
    *       defines rand(), srand() - random number generator
    *
    *******************************************************************************/
    
    #include <cruntime.h>
    #include <mtdll.h>
    #include <stddef.h>
    #include <stdlib.h>
    
    #ifndef _MT
    static long holdrand = 1L;
    #endif  /* _MT */
    
    /***
    *void srand(seed) - seed the random number generator
    *
    *Purpose:
    *       Seeds the random number generator with the int given.  Adapted from the
    *       BASIC random number generator.
    *
    *Entry:
    *       unsigned seed - seed to seed rand # generator with
    *
    *Exit:
    *       None.
    *
    *Exceptions:
    *
    *******************************************************************************/
    
    void __cdecl srand (
            unsigned int seed
            )
    {
    #ifdef _MT
    
            _getptd()->_holdrand = (unsigned long)seed;
    
    #else  /* _MT */
            holdrand = (long)seed;
    #endif  /* _MT */
    }
    
    /***
    *int rand() - returns a random number
    *
    *Purpose:
    *       returns a pseudo-random number 0 through 32767.
    *
    *Entry:
    *       None.
    *
    *Exit:
    *       Returns a pseudo-random number 0 through 32767.
    *
    *Exceptions:
    *
    *******************************************************************************/
    
    int __cdecl rand (
            void
            )
    {
    #ifdef _MT
    
            _ptiddata ptd = _getptd();
    
            return( ((ptd->_holdrand = ptd->_holdrand * 214013L
                + 2531011L) >> 16) & 0x7fff );
    
    #else  /* _MT */
            return(((holdrand = holdrand * 214013L + 2531011L) >> 16) & 0x7fff);
    #endif  /* _MT */
    }
    


  • Marc++us schrieb:

    😉
    Im Verzeichnis (z.B.) D:\Programme\Microsoft Visual Studio .NET\Vc7\crt\src findest Du eine Datei mit dem Namen "rand.c":

    und den code beabsichtige ich zu klauen.

    class Random
    {
       private:
       long holdrand;
       public:
       Random()
       {
          holdrand=time(0);
       }
       Random(unsigned int seed)
       {
          holdrand=seed;
       }
       int rand()
       {
            return(((holdrand = holdrand * 214013L + 2531011L) >> 16) & 0x7fff);
       }
    };
    

    nu kann sich jeder entscheiden, ob sein Random-Objet ein sigleton wird, ob es ne globale variable wird, ob es in der main() lebt oder was auch immer.

    und die folge ist reproduzierbarer als vorher, denn in meiner funktion mit meinem generator hab ich immer die gleiche folge, selbst wenn ich ne funktion aufrufe, die bis vor 2 tagen kein rand() benutzt hat, aber auf einmal es tut.



  • Erhard Henkes schrieb:

    Volkard: "zeig mal die implemetierung der rand()."
    Kann ich leider nicht, ist rand() aus der Standardbibliothek <cstdlib>.
    😕

    ich suche sowas am liebvsten, indem ich mit dem debugger vor die funktion laufe (beim msvc mit cursor über die zaile mit rand() gehen und f10 drücken) funktion hüpfe (beim msvc f11).



  • @Erhard:

    Evtl. wird Volkard das Ding auch noch serialisieren wollen... also sowas:

    class RandomPersistent
    { 
    ...
    public: 
       RandomPersistent() 
       { 
          ifstream in(bla);
          if (in)
             in >> holdrand; 
          else
             // bzw. bei Dateifehler (does not exist) dann 
             holdrand = time(0);
       } 
       ~RandomPersistent()
       {
          ofstream out(bla);
          out << holdrand;
       }
    ...
    };
    

    Ist doch wirklich ein Vorteil, daß der RandomGenerator inzwischen ein Template-Parameter ist. 😉



  • Das Konzeptionelle ist weitgehend klar. Ich muss mich nun mit verschiedenen Funktionen random() beschäftigen. Habe ich bisher nie getan, immer nur rand() aus <cstdlib> als Funktion eingesetzt. Ziel wäre z.B. eine verbesserte Gleichverteilung.



  • Soweit ich weiß ist im http://www.c-plusplus.net/titelanzeige.php?ISBN=3893193766 auch eine drin.


Anmelden zum Antworten