[gelöst] GSL und Random Number Generation
-
Hi Leute,
eine Frage zur Erzeugung von Zufallszahlen mit der GSL.
Durch die Unterstützung in den anderen Threads habe ich ja Erzeugung von Zufallszahlen erfolgreich ausgelagert.
Das habe ich bisher, also lediglich das Beispiel aus der GSL in eine eigenen Datei gepackt.
// rng.h #ifndef rng_h #define rng_h double get_random_number(); #endif
#include <stdio.h> // GSL #include <gsl/gsl_rng.h> // header files #include "rng.h" double get_random_number() { const gsl_rng_type* T; gsl_rng* r; gsl_rng_env_setup(); T = gsl_rng_default; r = gsl_rng_alloc (T); return gsl_rng_uniform (r); gsl_rng_free (r); }
Jetzt wollte ich mir 10 Zufallszahlen anzeigen lassen:
#include <iostream> #include "rng.h" using namespace std; int main(){// for(int i = 0; i < 10; i++) { cout << "Random number: " << get_random_number() << endl; } return 0; }
Leider kommt 10 mal das gleiche heraus.
:~/GSL/Cpp$ ./mainWater Random number: 0.999742 Random number: 0.999742 Random number: 0.999742 Random number: 0.999742 Random number: 0.999742 Random number: 0.999742 Random number: 0.999742 Random number: 0.999742 Random number: 0.999742 Random number: 0.999742 :~/GSL/Cpp$
Ich denke ich habe die Grundlagen eines Random Generators (RNG) verstanden, sodass ich der Meinung bin, dass es am seed liegt. Macht irgendwie auch Sinn, dass bei dem Abruf einer Zufallszahl mittels get_random_number() die Funktion aufgerufen (initialisiert?) wird, die Zufallszahl anhand des ersten Werts des seeds zurückgibt und die Funktion dann wieder verschwindet (zerstört) wird.
Das Problem ist wohl, dass das jedes Mal passiert, d.h. der seed hat keine Möglichkeit andere Zufallszahlen zurückzugeben als anhand des Startwerts.Aus diesem Grund arbeiten manche Zufallszahlen mit der Computerzeit als seed, um dafür zu sorgen, dass die zurückgegebene Zufallszahl bei jedem Aufruf anders ist.
Also laut der GSL kann man das scheinbar auch hier und dort einstellen.
Wobei der erste Link scheinbar den Seed bestimmt, also muss ich hier ansetzen?
Der zweite Link beschreibt scheinbar nur die Art des RNG, wovon es ja viele Arten gibt.Hat jemand Erfahrung mit der GSL und kann mir hier helfen? Oder ist das ein grundlegendes C++ Problem?
Viele Grüße und Danke im Voraus,
Klaus.
-
Klaus82 schrieb:
Oder ist das ein grundlegendes C++ Problem?
Das Problem ist, dass du die Möglichkeiten von C++ nicht nutzt. In C würde man den Zufallsgenerator static oder global machen (was in C++ natürlich auch möglich ist, beides hast du aber nicht getan), in C++ hast du aber die viel schönere Möglichkeit den Zufallsgenerator lokal in einem Klassenobjekt zu halten:
Ungetestet, sehr stark vereinfacht:
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);} };
Das schmückt man dann noch aus mit Funktionen die einen anderen Seed als den Standardseed setzen können, flexibleren Zufallszahlerzeugungsfunktionen, und so weiter, du weißt sicher besser als ich, was dir noch fehlt.
P.S.: Oder du nutzt die neuen Zufallszahlgeneratoren aus tr1, die in C++11 nun auch offiziell geworden sind (teilweise noch etwas buggy implementiert, aber das sollte sich schnell ändern).
-
Uff
Das muss ich jetzt erstmal wieder verdauen.
SeppJ schrieb:
Klaus82 schrieb:
Oder ist das ein grundlegendes C++ Problem?
Das Problem ist, dass du die Möglichkeiten von C++ nicht nutzt. In C würde man den Zufallsgenerator static oder global machen (was in C++ natürlich auch möglich ist, beides hast du aber nicht getan), in C++ hast du aber die viel schönere Möglichkeit den Zufallsgenerator lokal in einem Klassenobjekt zu halten:
Nun. Ich wollte die Zufallszahlen als Methode zur Verfügung stellen, sodass ich in jeder Methode wo ich sie benötige einfach rng.h includen muss und mir steht die Funktion get_random_number() zur Verfügung.
Ich denke das geht so in Richtung 'global' zur Verfügung machen, oder? Wobei ich immer unter 'global' verstanden habe, dass man es in die Hauptdatei einfach in die Präambel [1] schreibt.Warum man nun in C++ keine Funktionen in header Dateien deklariert, sondern sofort wieder eine Klasse daraus macht ist mir nicht ganz klar. Sieht zunächst nach Mehraufwand aus, wird sich später aber scheinbar bezahlt machen? vielleicht weil man einen Pointer drauflegen kann?
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);} };
Howdi! Das muss ich erstmal Stück für Stück verstehen, weil das meine Kenntnisse an Klassen und Syntax doch übersteigt.
Also zunächst deklarierst du einen Pointer vom Typ gsl_rnggsl_rng *rng;
Da du die Klasse RNG genannt hast wird RNG() und ~RNG() der Konstruktor bzw. Destruktor sein.
Für den Konstruktor sagst du zunächst, dass bei der Erzeugung eines Objekts vom Typ RNG eine konstante Referenz (Adresse?) von Typ RNG übergeben wird, kann man das so sagen?
Was mich dann allerdings wundert, dass Konstruktor und Destruktor zwei Mal vorkommen. Vor und nach public:. Ist das guter Programmierstil die beiden zunächst zu deklarieren und dann in dem public Bereich sie zu definieren?Und die Zeile
RNG(): rng(gsl_rng_alloc(gsl_rng_default)) {}
ist die Kurzform für
RNG(){// rng = gsl_rng_alloc(gsl_rng_defaul);}
Oder?
Junge, Junge! Was du allerdings mit dem operator() willst verstehe ich leider nicht.
SeppJ schrieb:
P.S.: Oder du nutzt die neuen Zufallszahlgeneratoren aus tr1, die in C++11 nun auch offiziell geworden sind (teilweise noch etwas buggy implementiert, aber das sollte sich schnell ändern).
Puh, das lasse ich am Besten zunächst. Muss ja noch einiges an Grundlagen lernen.
Gruß,
Klaus.[1] Sagt man das hier auch so? Kenne das von LaTex, das ist dann alles vor \begin{document}. Bei C++ wäre das für mich alles vor int main(){}.
-
Klaus82 schrieb:
Warum man nun in C++ keine Funktionen in header Dateien deklariert, sondern sofort wieder eine Klasse daraus macht ist mir nicht ganz klar. Sieht zunächst nach Mehraufwand aus, wird sich später aber scheinbar bezahlt machen? vielleicht weil man einen Pointer drauflegen kann?
Du hast hier eine ganze Reihe Funktionen, die alle auf dem selben Zufallsgenerator operieren. Du möchtest Seeds setzen, Zahlen generieren, usw. Eine einzelne Funktion kann das nicht leisten, du brauchst mehrere. Diese haben aber die Gemeinsamkeit, alle auf dem gleichen gemeinsamen Objekt zu arbeiten. Lauter kann man gar nicht nach dem Wort "Klasse" schreien.
In C++ hat man nichts gegen Funktionen, aber wenn du hier die Nützlichkeit von Klassen nicht siehst, dann fehlt die wohl jegliches Wissen zu Objektorientierung.
[...Jede Menge Nachfragen zu C++ Grundlagen...]
Puh, das lasse ich am Besten zunächst. Muss ja noch einiges an Grundlagen lernen.
Ja, das ist wohl eine gute Idee.
Wobei ich immer unter 'global' verstanden habe, dass man es in die Hauptdatei einfach in die Präambel [1] schreibt.
[1] Sagt man das hier auch so? Kenne das von LaTex, das ist dann alles vor \begin{document}. Bei C++ wäre das für mich alles vor int main(){}.
Nein, das sagt man nicht so und da besteht auch keine nennenswerte Ähnlichkeit zu LaTeX, außer ich hätte LaTeX bisher völlig falsch verstanden.
-
SeppJ schrieb:
Klaus82 schrieb:
[...Jede Menge Nachfragen zu C++ Grundlagen...]
Puh, das lasse ich am Besten zunächst. Muss ja noch einiges an Grundlagen lernen.
Ja, das ist wohl eine gute Idee.
Gut, da muss ich natürlich noch durch.
Wobei ich mir nicht sicher bin, ob es mein Kernproblem lösen würde, denn du schreibst ja selbst.
SeppJ schrieb:
Das schmückt man dann noch aus mit Funktionen die einen anderen Seed als den Standardseed setzen können, flexibleren Zufallszahlerzeugungsfunktionen, und so weiter, du weißt sicher besser als ich, was dir noch fehlt.
Also wie muss ich den Seed für die GSL setzen, dass ich beim Funktionsaufruf auch jedes Mal eine andere Zufallszahl erhalte.
So die Frage, ob Leute hier Erfahrung mit der GSL haben, um mir da weiterhelfen zu können.Viele Grüße,
Klaus.
-
Klaus82 schrieb:
Also wie muss ich den Seed für die GSL setzen, dass ich beim Funktionsaufruf auch jedes Mal eine andere Zufallszahl erhalte.
Du hast im ersten Post doch selbst drauf gelinkt: gsl_rng_set()
-
Nein, du darfst den Seed eben nicht jedes Mal neu setzen! Das ist doch genau das Problem, dass du das derzeit tust!
Ich glaube, du solltest dir auch mal anschauen, wie ein PRNG funktioniert. Und Grundlagen von C wären vielleicht gut, wenn es mit C++ nicht geht. Jedenfalls habe ich den Eindruck, dass du gar nicht die Konzepte hinter der GSL verstehst und als ich sie dir erklären wollte, hast du es auch nicht verstanden. Du brauchst einigermaßen solides Hintergrundwissen, um fremde Bibliotheken nutzen zu können.
-
Caligulaminus schrieb:
Klaus82 schrieb:
Also wie muss ich den Seed für die GSL setzen, dass ich beim Funktionsaufruf auch jedes Mal eine andere Zufallszahl erhalte.
Du hast im ersten Post doch selbst drauf gelinkt: gsl_rng_set()
Ja,
nur weiß ich eben nicht, was ich da hineinschreiben muss. Beim C/C++ internen random generator gibt ja die Zeilesrand ( time(NULL) );
den seed vor. In diesem Beispiel wird die Systemzeit vewendet.
Analog müsste ich für die GSL eine entsprechende Zeile formulieren für gsl_rng_set().
SeppJ schrieb:
Nein, du darfst den Seed eben nicht jedes Mal neu setzen! Das ist doch genau das Problem, dass du das derzeit tust!
Richtig. Dass dies mein Problem ist hatte ich schon im ersten Beitrag geschrieben.
Und genau diese Problemstellung würde ich gerne lösen.SeppJ schrieb:
Ich glaube, du solltest dir auch mal anschauen, wie ein PRNG funktioniert. Und Grundlagen von C wären vielleicht gut, wenn es mit C++ nicht geht.
Bei allem Respekt, aber das ist eine sehr pauschale Aufforderung, der es schwer ist zu begegnen.
Ich denke die Grundlagen eines PRNG habe ich verstanden, zumindest von der numerischen Seite her. Ich denke das zeigt sich z.B. daran, dass ich vermutet hatte, dass es am seed liegt.
Das wiederum zeigt auch grundlegendes Verständnis für C/C++, schließlich habe ich ja auch beschrieben, dass meine rng jedes mal neu erzeugt und wieder vernichtet wird, d.h. der seed hat gar keine Möglichkeit weiterzulaufen.Ich denke ich kann guten Wissens behaupten, dass ich zumindest versuche zu verstehen was ich tue, sonst würde ich hier nicht fragen. Ich schreibe ja auch keine Posts im Stile von "Macht mal, ich brauche das bis morgen - es muss nur funktionieren."
Ich schreibe was ich habe, was mich mir dabei denke und wo meiner Meinung nach die Probleme liegen.
Ich denke also schon, dass ich gewisse Grundlagen vorzuweisen habe. Ich meine bei allem Respekt, eine Einführung von C fängt i.d.R. auch mit einem Hello World Programm an und nicht mit der Klärung von namespace, der int main() Umgebung oder den Speicheradressen, wo das Programm liegt.Aus dem Grund ist es sicherlich so, dass ich ein gefährliches Halbwissen habe. Genug um was zusammenzubasteln und Fragen und eigene Gedanken zu formulieren, aber leider kein solides Fundament auf dem das aufbaut.
Nur das ist eben meine Situation. Ich benutze C++ in der Physik, um damit zu arbeiten und versuche nun eben selbst C++ besser zu lernen und zu verstehen (und Kompiler und die Numerik).
Gruß,
Klaus.
-
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 Typdouble 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 obenconst 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 UmwegT = 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.
-
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 einfachdouble 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.
-
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.
-
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.hIn 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 sop->();
Wir müssen doch so nah dran sein!
Gruß,
Klaus.
-
Drei Methoden:
-Zufallsgenerator global machen
-Zufallsgenerator als Member einer Klasse
-Zufallsgenerator in einen Scope über den aufgerufenen Funktionen machen und immer herumreichenAlle 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.
-
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.
-
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.