[gelöst] GSL und Random Number Generation



  • Okay, okay.

    Ich habe nochmal nachgelesen.

    SeppJ schrieb:

    class RNG 
    {
     gsl_rng *rng;
    
      // Ich bin faul, daher verbiete ich für dieses Beispiel Kopien und Zuweisungen
      // Diese Funktionen würdest du normalerweise sinnvoll definieren und public machen:
      RNG(const RNG &);
      RNG& operator=(const RNG &);
    public:
     RNG(): rng(gsl_rng_alloc(gsl_rng_default)) {}
     ~RNG () {gsl_rng_free(rng);}
     double operator() () {return gsl_rng_uniform(rng);}
    };
    

    Holen wir noch einmal das GSL Beispiel hervor, denn dazu habe ich gleich wieder eine wahrscheinlich triviale Frage.

    const gsl_rng_type * T;
    
           gsl_rng_env_setup();
    
           T = gsl_rng_default;
           r = gsl_rng_alloc (T);
    

    Frage:
    Ich hatte extra nochmal über Zeiger nachgelesen und deshalb irriert mich der Syntax. Normalerweise wird doch zunächst der Typ geklärt, auf den der Zeiger weisen soll und dann findet die Adresszuweisung statt (in Zeigern speichere ich schließlich nur Adressen). Also vom Typ

    double value = 5;
    double* p;
    p = & value;
    

    Also wenn ich eine Adresse einem Zeiger zuweisen möchte, so benötige ich doch den & Operator ("gib mir die Adresse von").
    Also warum funktioniert oben

    const gsl_rng_type * T;
     T = gsl_rng_default;
    

    Müsste es nicht heißen

    const gsl_rng_type* T;
    T = & gsl_rng_default;
    

    Ich nehme also an, dass gsl_rng_default per so schon eine Adresse (oder Zeiger?) zurückgibt?

    Jetzt ist mir auch die Definition deines Konstruktors ein wenig klarer. Du packst all die vier Zeilen Programmcode in eine.
    Du machst z.B. nicht den Umweg

    T = gsl_rng_default;
    
    gsl_rng_uniform(T);
    

    Sondern packst das direkt zusammen

    gsl_rng_uniform(gsl_rng_default)
    

    .

    Jetzt habe ich auch die Erklärung für den Operator hier gefunden. Es soll folgendes machen

    Der Aufrufoperator ermöglicht es, ein Objekt der Klasse als Funktion aufzurufen.

    Okay, verstehe.

    Ich versuche es dann mal nachzubasteln.

    Wenn ich dann die Klasse verwenden möchte, sprich eine Zufallszahl in time_of_photon_income(), dann muss ich dort eine Instanz der Klasse RNG initialsieren, oder?

    Löst das dann aber mein Problem mit dem seed? Schließlich wird die Instanz dann auch immer erzeugt und wieder vernichtet

    Auf jeden Fall für das Verständnis wieder einen großen Schritt weiter! 🙂

    Gruß,
    Klaus.


  • Mod

    Das ist im wesentlichen richtig.

    Löst das dann aber mein Problem mit dem seed? Schließlich wird die Instanz dann auch immer erzeugt und wieder vernichtet

    Die Idee ist, immer das gleiche Objekt zu benutzen.



  • Hi,

    SeppJ schrieb:

    Löst das dann aber mein Problem mit dem seed? Schließlich wird die Instanz dann auch immer erzeugt und wieder vernichtet

    Die Idee ist, immer das gleiche Objekt zu benutzen.

    Mh.

    Also ich habe jetzt mal einen Zwischenerfolg erzielt. Ich habe anhand deines Vorschlags eine Klasse definiert.

    // rng_2.h
    
    #ifndef RNG_2_H
    #define RNG_2_H
    
    #include <gsl/gsl_rng.h>
    
    class RNG{//
    public:
       RNG();
       ~RNG();
       gsl_rng* rng;
       double operator() ();
    
    };
    
    RNG::RNG()
    { rng = gsl_rng_alloc(gsl_rng_default); }
    
    RNG::~RNG()
    { gsl_rng_free(rng); }
    
    double RNG::operator() ()
    { return gsl_rng_uniform(rng); }
    
    #endif
    

    Allerdings muss ich zugeben, dass ich deine beiden Zeilen

    RNG(const RNG &);
    RNG& operator=(const RNG &);
    

    nach wie vor nicht verstehe. Ich habe nochmal über Klassen nachgelesen und dort steht noch einiges zum this Zeiger, ich nehme an darum geht es.
    Denn eigentlich würde jede Elementfunktion (also auch der Konstruktor) verborgen stets ein &<Instanz> mitschreiben, also z.B. anstatt einfach

    double function(double a, double b)
    

    würde dann daraus

    double function (&<Instanz>, double a, double b)
    

    Nachdem es auch so funktioniert lasse ich es erstmal draußen.

    Schließlich binde ich das ganze wieder in meine mainWater Datei ein

    #include <iostream>
    
    #include "rng_2.h"
    
    using namespace std;
    
    int main(){//
    
       class RNG random;
    
       for(int i = 0; i < 10; i++)
       {
          cout << "Random Number: " << random() << endl;
       }
    
    return 0;
    
    }
    

    Und daraus wird dann:

    :~/GSL/Cpp$ ./mainWater 
    Random Number: 0.999742
    Random Number: 0.16291
    Random Number: 0.282618
    Random Number: 0.947201
    Random Number: 0.231657
    Random Number: 0.484974
    Random Number: 0.957477
    Random Number: 0.744305
    Random Number: 0.540044
    Random Number: 0.739953
    

    Jetzt muss ich das nur nochmal beim Verschachteln hinkriegen, wie ich da nur ein Objekt erzeuge.
    Oder ich erzeuge das Objekt in der Hauptdatei mainWater.cpp und übergebe dann einen Zeiger an das Unterprogramm, dann würde es nicht jedes Mal erzeugt und vernichtet werden. Ich bastel mal...

    Gruß,
    Klaus.


  • Mod

    Klaus82 schrieb:

    Allerdings muss ich zugeben, dass ich deine beiden Zeilen

    RNG(const RNG &);
    RNG& operator=(const RNG &);
    

    nach wie vor nicht verstehe.

    Ich habe bloß den Kopierkonstruktor und den Zuweisungsoperator private gemacht, da die Klasse nicht trivial kopierbar ist. Sonst hättest du beim herumspielen mit dem Beispiel eventuell Fehler gemacht. Die beiden Methoden würde ich normalerweise public machen und richtig implementieren. Aber dazu hätte ich noch die gsl-Doku rausholen müssen, wie das mit dem Kopieren von Objekten geht. Die GSL-Zufallszahlengeneratoren sind nämlich auch auf gewisse Weise objektorientiert, aber da die C-Schnittstelle keine Sprachunterstützung für Objektorientierung bietet, muss man eben alles selber machen und höllisch aufpassen, keine Fehler zu machen. Das ist auch ein weiterer Grund für die Klasse aus dem Beispiel: Wenn man diese Methoden erst einmal vernünftig implementiert hat (also so, wie die GSL es will), dann übernimmt C++ (das unterstützt Objektorientierung von Natur aus) den ganzen Rest und man kann gar nichts mehr falsch machen.

    Das ging aber über den Aufwand hinaus, schnell am Samstagmorgen ein kleines Beispiel in einem Forum zu bringen.



  • Hi SeppJ,

    danke für die ausführliche Antwort! 🙂

    Ich sehe schon, es macht Sinn sich vermehrt mit C++ zu beschäftigen.

    Umgekehrt habe ich es jetzt auch geschafft den seed in die Verwendung von rng.h einzusetzen, ich bin ja ein bissel stolz auf mich. 😉

    Ich habe nochmal auf der GSL Homepage nachgeschaut und bei C++ geschaut, wie die seeden.

    rng.h                                                              
    
    // rng.h
    
    #ifndef RNG_2_H
    #define RNG_2_H
    
    #include <time.h>
    #include <gsl/gsl_rng.h>
    
    class RNG{//
    public:
       RNG();
       ~RNG();
       gsl_rng* rng;
       double operator() ();
    
    };
    
    RNG::RNG()
    { 
       rng = gsl_rng_alloc(gsl_rng_default);
       gsl_rng_set (rng,time(NULL));
     }
    
    RNG::~RNG()
    { gsl_rng_free(rng); }
    
    double RNG::operator() ()
    { return gsl_rng_uniform(rng); }
    
    #endif
    

    Das Problem ist nur, dass es nach wie vor keine Veränderung bringt! 😡 😞

    Kann es sein, dass das Programm schneller nach Zufallszahlen fragt, als die Zeit sich ändert, um eine andere Zahl für den seed zu generieren? 😕
    Na ja, hinzu kommt noch, dass in dem C++ Beispiel wieder nur einmal initialisiert wird! Also das alte Problem.

    Och man...

    Gruß,
    Klaus.


  • Mod

    Das klingt so, als würdest du dauernd neue Klasseninstanzen erzeugen.



  • SeppJ schrieb:

    Das klingt so, als würdest du dauernd neue Klasseninstanzen erzeugen.

    Jap,
    weil ich nach wie vor bei der Verschachtelung bin. Hab jetzt zwar das Linken gelöst aber dafür dieses Problem.

    Ich habe bisher drei Ebenen, wenn ich das so nennen darf.
    Die main Datei, time_of_photon_income() und rng.h

    In der main Datei wird nach der Zeit gefraft, d.h. mainWater.cpp ruft time_of_photon_income() auf.
    Diese Funktion benötigt allerdings für ihre Ausübung eine Zufallszahl, sprich time_of_photon_income() ruft rng.h auf, genau gesagt dessen operator().

    Also jedes Mal, wenn ich eine Zufallszhal möchte, erzeuge ich alles, nur um es danach wieder zu vernichten.

    Ich versuche das ganze ja als cpp Dateien separat zu kompilieren, um es dann im Programm nach belieben verwenden zu können.

    Nur irgendwie ist das dann scheinbar der Nachteil?

    Oder wie gesagt, ich muss ganz zu Beginn der mainWater.cpp Datei einmal RNG initialisieren und dann immer pointer darauf weitergeben.

    Dann hätte ich etwas in der Art

    #include "rng.h"
    #include "time_of_photon_income.h"
    
    using namespace std;
    
    int main(){//
    
    class RNG random;
    class RNG* p;
    
    cout << "Time: " << time_of_photon_income(p) << " fs" << endl;
    }
    

    Und time_of_photon_income() muss dann entsprechend modifiziert werden.

    double time_of_photon_income(class RNG* p){//
    }
    

    Wobei ich hier wieder am Schwimmen bin, ob ich nicht in der Definition der Funktion noch den Adressoperator benötigen würde, also eher

    double time_of_photon_income(class RNG* p &){//
    

    Und wie würde ich dann den Operator () aufrufen? 😕
    Einfach so

    p->();
    

    😕 😕

    Wir müssen doch so nah dran sein! 😃

    Gruß,
    Klaus.


  • Mod

    Drei Methoden:
    -Zufallsgenerator global machen
    -Zufallsgenerator als Member einer Klasse
    -Zufallsgenerator in einen Scope über den aufgerufenen Funktionen machen und immer herumreichen

    Alle Methoden repräsentieren jeweils ein unterschieldiches Konzept, wo der Zufallsgenerator lebt. Man kann nicht sagen, was besser oder schlechter ist, nur, ob es besser zu dem passt, was du möchtest oder nicht (Wobei Methode 3 ein bisschen wie Methode 2 für Arme ist).

    Und ich bringe dir jetzt nicht die Syntax der Sprache, noch die Grundlagen von Klassen bei. Vielleicht findest du jemand anderen dafür, aber ich beschränke mich darauf, auf Lehrbücher zu verweisen.



  • Na gut,

    einen letzten Anlauf unternehme ich noch.

    Nachdem ich mit dem Syntax für den () Operator nicht zurechtgekommen bin, habe ich eine weitere Elementfunktion definiert get_random_number(), meine rng.h sieht jetzt so aus:

    // rng.h
    
    #ifndef RNG_H
    #define RNG_H
    
    #include <time.h>
    #include <gsl/gsl_rng.h>
    
    class RNG{//
    public:
    	RNG();
    	~RNG();
    	gsl_rng* rng;
    	double get_random_number();
    	double operator() ();
    
    };
    
    RNG::RNG()
    { 
    	rng = gsl_rng_alloc(gsl_rng_default);
    	gsl_rng_set (rng,time(NULL));
     }
    
    RNG::~RNG()
    { gsl_rng_free(rng); }
    
    double RNG::operator() ()
    { return gsl_rng_uniform(rng); }
    
    double RNG::get_random_number()
    { return gsl_rng_uniform(rng); }
    
    #endif
    

    Das ganze klapp jetzt auch wunderbar in meiner mainWater.cpp Datei, wenn ich eine Instanz initialisiere, einmal mit . Operator oder mittels eines Pointers.

    #include <iostream>
    
    #include "rng.h"
    
    using namespace std;
    
    int main(){//
    
    //	class	RNG random;
    
    // for(int i = 0; i < 10; i++)
    //	{
    //		cout << "Random Number: " << random.get_random_number() << endl;
    //	}
    
    	class RNG* pointer = new RNG;
    
    	for(int i = 0; i < 10; i++)
    	{
    		cout << "Random Number: " << pointer->get_random_number() << endl;
    	}
    
    return 0;
    
    }
    
    :~/GSL/Cpp$ g++ -Wall -pedantic -ansi -c mainWater.cpp
    :~/GSL/Cpp$ g++ -L/usr/local/lib -o mainWater mainWater.o -lgsl -lgslcblas -lm
    :~/GSL/Cpp$ ./mainWater 
    Random Number: 0.0408848
    Random Number: 0.715352
    Random Number: 0.0940853
    Random Number: 0.0101064
    Random Number: 0.226571
    Random Number: 0.762686
    Random Number: 0.363975
    Random Number: 0.32461
    Random Number: 0.994962
    Random Number: 0.805768
    

    Jetzt habe ich meine time_of_photon_income.h und .cpp soweit modifiziert, dass ein Pointer der Klasse RNG übergeben wird

    // time_of_photon_income.h
    
    #ifndef TIME_OF_PHOTON_INCOME_H
    #define TIME_OF_PHOTON_INCOME_H
    
    double time_of_photon_income(class RNG* p);
    
    #endif
    

    Und

    #include <math.h>
    
    #include "rng.h"
    #include "time_of_photon_income.h"
    
    double time_of_photon_income(class RNG* p){//
    
    double mu = 12.5;
    double sigma =	5/sqrt(2*log(2));
    
    bool condition = 0;
    
    double rand1 = p->get_random_number();
    double rand2 = p->get_random_number();
    
    double income_time = 0;
    
    	while(condition == 0)
    	{	
    		income_time = mu + sigma * sqrt(-2*log(rand1)) * cos(2*M_PI*rand2);
    
    		if( 0 < income_time && income_time < 25)
    		{ condition = 1; }
    	}
    
    return income_time;
    
    }
    

    Was mich jetzt aber verwundert, dass ich Probleme mit dem Linker bekomme, weil Sachen mehrfach definiert wären. Dafür habe ich doch die header Dateien extra ge-guarded, oder nicht?

    :~/GSL/Cpp$ g++ -Wall -pedantic -ansi -c time_of_photon_income.cpp
    :~/GSL/Cpp$ g++ -L/usr/local/lib -o mainWater mainWater.o time_of_photon_income.o -lgsl -lgslcblas -lm
    time_of_photon_income.o: In function `RNG::RNG()':
    time_of_photon_income.cpp:(.text+0x0): multiple definition of `RNG::RNG()'
    mainWater.o:mainWater.cpp:(.text+0x0): first defined here
    time_of_photon_income.o: In function `RNG::RNG()':
    time_of_photon_income.cpp:(.text+0x44): multiple definition of `RNG::RNG()'
    mainWater.o:mainWater.cpp:(.text+0x44): first defined here
    time_of_photon_income.o: In function `RNG::~RNG()':
    time_of_photon_income.cpp:(.text+0x88): multiple definition of `RNG::~RNG()'
    mainWater.o:mainWater.cpp:(.text+0x88): first defined here
    time_of_photon_income.o: In function `RNG::~RNG()':
    time_of_photon_income.cpp:(.text+0xa6): multiple definition of `RNG::~RNG()'
    mainWater.o:mainWater.cpp:(.text+0xa6): first defined here
    time_of_photon_income.o: In function `RNG::operator()()':
    time_of_photon_income.cpp:(.text+0xc4): multiple definition of `RNG::operator()()'
    mainWater.o:mainWater.cpp:(.text+0xc4): first defined here
    time_of_photon_income.o: In function `RNG::get_random_number()':
    time_of_photon_income.cpp:(.text+0xf4): multiple definition of `RNG::get_random_number()'
    mainWater.o:mainWater.cpp:(.text+0xf4): first defined here
    collect2: ld returned 1 exit status
    

    Könntest du mir dabei bitte noch helfen, dann geh ich in mein stilles Kämmerchen lesen ...

    Gruß,
    Klaus.


  • Mod

    Includeguards helfen nicht gegen mehrfache Definitionen. Die sind gegen mehrfache Deklaration. Daher: Keine Definitionen in Header oder wenn doch, dann inline.



  • SeppJ schrieb:

    Includeguards helfen nicht gegen mehrfache Definitionen. Die sind gegen mehrfache Deklaration. Daher: Keine Definitionen in Header oder wenn doch, dann inline.

    Okay,
    meine rng.h sieht also wie folgt aus:

    // rng.h
    
    #ifndef RNG_H
    #define RNG_H
    
    #include <time.h>
    #include <gsl/gsl_rng.h>
    
    class RNG{//
    public:
    	inline	RNG();
    	inline	~RNG();
    	gsl_rng* rng;
    	inline	double get_random_number();
    	inline	double operator() ();
    
    };
    
    RNG::RNG()
    { 
    	rng = gsl_rng_alloc(gsl_rng_default);
    	gsl_rng_set (rng,time(NULL));
     }
    
    RNG::~RNG()
    { gsl_rng_free(rng); }
    
    double RNG::operator() ()
    { return gsl_rng_uniform(rng); }
    
    double RNG::get_random_number()
    { return gsl_rng_uniform(rng); }
    
    #endif
    

    Und jetzt funktioniert es auch in der mainWater.cpp mit der Übergabe des Pointers.

    #include <iostream>
    
    #include "rng.h"
    #include "time_of_photon_income.h"
    
    using namespace std;
    
    int main(){//
    
    //	class	RNG random;
    
    // for(int i = 0; i < 10; i++)
    //	{
    //		cout << "Random Number: " << random.get_random_number() << endl;
    //	}
    
    	class RNG* pointer = new RNG;
    
    	for(int i = 0; i < 10; i++)
    	{
    		cout << "Random Number: " << time_of_photon_income(pointer) << endl;
    	}
    
    return 0;
    
    }
    
    :~/GSL/Cpp$ ./mainWater 
    Random Number: 10.0351
    Random Number: 13.3335
    Random Number: 10.5408
    Random Number: 5.31796
    Random Number: 18.6461
    Random Number: 14.3153
    Random Number: 14.1575
    

    Das war es also? 😮

    SeppJ du bist der beste, tausend Dank, dass du trotz all meiner Fragen bei mir geblieben bist!! 🙂 🙂 👍 👍

    Viele Grüße,
    Klaus.


  • Mod

    Da ist aber immer noch einiges falsch:
    -Du musst unbedingt einen Kopierkonstruktor und Zuweisungsoperator definieren. Du hast gerade Glück, dass es auch ohne funktioniert. Google mal "Regel der großen Drei".
    -Du hast immer noch Funktionsdefinitionen mit externer Bindung im Header. Das macht man, wie gesagt, nicht. Du hast nur zufällig den Header bloß ein einziges Mal benutzt, daher funktioniert es ausnahmsweise.
    -Den mit new angeforderten Generator gibst du nie frei.

    Stilistische Fehler:
    -Warum erstellst du überhaupt den Zufallsgenerator mittels new, wenn er doch in der main lebt? Dann kannst du doch dort auch einfach eine lokale Variable anlegen.*
    -Das class vor RNG in Zeile 17 braucht man in C++ nicht, daher macht man es auch nicht.

    *: Da du den Generator dadurch nie kopierst, geht der fehlende Kopierkonstruktor durch. Die zwei Fehler heben sich hier quasi weg.



  • Mein lieber Herr Gesangsverein, jetzt machst du hier aber noch ein Fass auf! 😃

    Nicht falsch verstehen, ich bin ja froh darum. 🙂

    Aber ich dachte ich hätte es jetzt 'geschafft', weil es klappt. Und jetzt kriege ich hier um die Ohren geschlagen, dass es eigentlich nur Zufall ist! 😮 😉

    Langsam habe ich auch eine Ahnung davon was du unter Grundlagen verstehst. Nicht einfach den Syntax von C++ kennen, um einen Code runterschreiben zu können, sondern auch viel konzeptionelles dahinter im Zusammenhang mit Kompiler und Linker.

    Wäre wahrscheinlich ähnlich wenn sich sich ein Physiker auf den Standpunkt stellt, dass er Physik machen möchte und keine Mathe. 😃

    SeppJ schrieb:

    Du musst unbedingt einen Kopierkonstruktor und Zuweisungsoperator definieren. Du hast gerade Glück, dass es auch ohne funktioniert. Google mal "Regel der großen Drei".

    Jau, habe danach gesucht und gleich den ersten Eintrag von Wikipedia genommen.
    Jetzt verstehe ich auch besser die Zeile aus deiner Klasse:

    RNG(const RNG &);
    RNG& operator=(const RNG &);
    

    Das ist der Kopierkonstruktor! 🙂

    SeppJ schrieb:

    Du hast immer noch Funktionsdefinitionen mit externer Bindung im Header.

    Wie meinen? Es haben doch alle Funktionsdefinitionen der Klasse ein inline? Oder verwechsel ich das jetzt?

    Man man man...

    Gruß,
    Klaus.


  • Mod

    Klaus82 schrieb:

    Wie meinen? Es haben doch alle Funktionsdefinitionen der Klasse ein inline? Oder verwechsel ich das jetzt?

    Nein, ich hab das inline bloß nicht gesehen. Ich bin schon so gewohnt, dass man

    class Foo
    {
     void bar() { /* Implementierung von foo::bar() */ }
    };
    

    schreibt, wenn man inline meint, dass ich nach deiner Methode gar nicht richtig geguckt habe.



  • Okay,

    also für meine Verhältnisse alles 'richtig'. 😉

    Dann bin ich wieder beruhigt. 🙂

    Gruß,
    Klaus.


Anmelden zum Antworten