Klasse für Nullstellenfindung



  • Hallo liebe Community. Ich habe gerade erst mit dem Programmieren angefangen und bin mir noch ziemlich unsicher mit meiner ersten Klassen-Programmierung. Ist jemand so freundlich mir zu sagen, ob mein derzeitiger Code folgender Aufgabenstellung entspricht?

    *Die Klasse Nullstelle soll einen Zeiger auf eine Funktion f, zwei Variablen vom Typ double für die Intervallränder a und b eines abgeschlossenen Intervalls [a,b] in dem nach einer Nullstelle von f gesucht wird, sowie eine Genauigkeitsschranke i_max und eine maximale Anzahl von Iterationen imax für die
    Nullstellensuche als Membervariablen haben.

    Man implementiere den Konstruktor sowie geeignete set- und get-Methoden zur Erzeugung und Initialisierung von Objekten der Klasse sowie zum Zugriff auf deren Membervariablen.*

    Im Anschluss mein Versuch der Aufgabe gemäß meine Klasse zu erstellen:

    #ifndef _NULLSTELLE_H
    #define _NULLSTELLE_H
    class Nullstelle {
    
    	private:
    	double *Zeiger (double);                    //Zeiger auf Funktion f; f liegt in [a,b] und ist daher von Typ double und gibt ein (double) Ergebnis.
        double Untergrenze, Obergrenze;             //Intervallränder eines abgeschlossenen Intervalls [a,b]
        double epsilon;                             //Genauigkeitsschranke epsilon
        int i_max;                                  //maximale Anzahl Iterationen i_max für Nullstellensuche
    
        public:
        Nullstelle(double, int);	                //Deklaration des Konstruktors
        Nullstelle(double);
        Nullstelle(int);
        Nullstelle();
    
        void set_Zeiger(double *Zeiger (double));   //geeignete Set- und Get- Methoden
        double get_Zeiger();
        void set_Untergrenze(double a);
        double get_Untergrenze();
        void set_Obergrenze(double b);
        double get_Obergrenze();
    	void set_epsilon(double epsilon);  
        double get_epsilon();
    	void set_i_max(int i_max);
        int get_i_max();
    
    };
    
    #endif
    

    Kritik und Hinweise darauf, was ich hätte besser machen können, sind natürlich gerne willkommen 🙂


  • Mod

    sowie eine Genauigkeitsschranke i_max

    Ich vermute, dies ist ein Schreibfehler und es soll epsilon heißen, oder?

    Zum Code:
    Bezeichner mit einem Unterstrich am Anfang gefolgt von einem Großbuchstaben sind überall reserviert und sollten nicht benutzt werden. Ebenso sind alle Bezeichner, die überhaupt mit einem Unterstrich losgehen, im globalen Namensraum reserviert.

    Was sollen die ganzen verschiedenen Konstruktoren? So ist das bestimmt nicht gemeint. Das macht auch keinen erkennbaren Sinn.

    Du solltest const-Correctness beachten.

    Die verlangte Implementierung fehlt noch.

    Warum eine Nullstellensuche überhaupt eine Klasse sein sollte, kann dir wohl nur dein Lehrer erklären. Oder auch nicht. Denn es sollte keine Klasse sein.



  • SeppJ schrieb:

    sowie eine Genauigkeitsschranke i_max

    Ich vermute, dies ist ein Schreibfehler und es soll epsilon heißen, oder?

    Ja, das sollte ein Epsilon sein, sorry^^'

    SeppJ schrieb:

    Zum Code:
    Bezeichner mit einem Unterstrich am Anfang gefolgt von einem Großbuchstaben sind überall reserviert und sollten nicht benutzt werden. Ebenso sind alle Bezeichner, die überhaupt mit einem Unterstrich losgehen, im globalen Namensraum reserviert.

    Welche Bezeichner meinst du damit? Falls du die set_Obergrenze etc. damit meinst, in meinem Buch stand, dass man seine private Membervariablen damit im public Bereich erst "verfügbar" macht, inklusive Unterstrich und ich hatte im Internet auch ein Beispiel mit dieser Schreibweise gesehen 😕

    SeppJ schrieb:

    Was sollen die ganzen verschiedenen Konstruktoren? So ist das bestimmt nicht gemeint. Das macht auch keinen erkennbaren Sinn.

    Auch das hab ich aus meinem Buch. Wie könnte man den Konstruktor denn besser formulieren?

    SeppJ schrieb:

    Du solltest const-Correctness beachten.

    Ah, das hatte ich ganz vergessen, komm mit dem const- Befehl noch etwas durcheinander.

    SeppJ schrieb:

    Die verlangte Implementierung fehlt noch.

    Ich dachte mit Implementierung wäre die Einfügung in den public-Teil der Klasse gemeint. Anscheinend nicht, was dann? 😕

    SeppJ schrieb:

    Warum eine Nullstellensuche überhaupt eine Klasse sein sollte, kann dir wohl nur dein Lehrer erklären. Oder auch nicht. Denn es sollte keine Klasse sein.

    Die Klasse zu erstellen ist erst der Anfang meines Projekts. Im späteren Teil soll ich einmal mittels friend-Funktion und anhand eines Beispiels die Klasse testen, allerdings ist die Klasse richtig zu programmieren derzeit mein größtes Problem an dem Projekt :S



  • Sieht doch ganz gut aus. Etwas kritisch sehe ich die Implementierung deiner Konstruktoren. Objekte einer Klasse sollten nach ihrer Erzeugung (durch einen Konstruktor) keine unitialisierten Daten mehr enthalten:
    Trotz vier Konstruktoren gibst du dem Anwender der Klasse nicht einmal die Möglichkeit die Unter,- und Obergrenze zu initialisieren und zwingst damit den schreibfaulen Programmierer die entsprechende Get/- Set Methode explizit aufzurufen 😃 .

    Kurz gesagt: Alle privaten Daten sind für die Funktionalität erforderlich und deshalb reicht ein Konstruktor.

    Nullstelle(((double *fp)(double)), double uGrenze, double oGrenze, double eps, int max);
    

    Weiterhin hast du jetzt auch die Möglichkeit eine einzige Konstruktorinterne Liste (falls du schon weißt, was das ist) zu verwenden: Etwas effizienter.

    Und weil du mich jetzt in den kritischen Modus gebracht hast, muss ich noch folgendes bemängeln:

    double *Zeiger (double); 
    void set_Zeiger(double *Zeiger (double));   //geeignete Set- und Get- Methoden
    double get_Zeiger();
    

    Die Aufgabenstellung besagt ja bereits, dass es sich bei dem Funktionsverweis um die mathematische Funktion selbst handelt, in der basierend auf die vorhandene Unter,- und Obergrenze nach Nullstellen gesucht werden soll.

    double *NullstellenFunktion (double); // vorher: double* Zeiger(double)
    void set_NullstellenFunktion(double *Zeiger (double)); // vorher: set_Zeiger
    double get_Nullstellen() // Hier findet die Berechnung der Nullstellen von *NullstellenFunktion statt
    

    Spätestens wenn du den tatsächlichen Code implementiert wirst du feststellen, dass der Rückgabetyp double nicht wirklich geeignet für get_Nullstellen() ist.
    ... Ich hab jetzt irgendwie keine Lust mehr zu schreiben, schau dir dann einfach die C++-Vektor Klasse an 😃 .



  • @Fernbedienung: Danke für die Hilfe 🙂

    Hab jetzt denke ich besser verstanden wie Konstruktoren funktionieren.
    Allerdings gibt mir das Programm eine Fehlermeldung, wenn ich (double *fp)(double) hinschreibe, weil es mit den vielen Klammern irgendwie nicht klar kommt...

    Deine Formulierung mit Nullstellenfunktion klingt definitiv besser 🙂 War mir bei der Zeigerdefinition ohnehin ziemlich unsicher, ich glaube da muss ich noch etwas mehr zu lernen 😃

    void set_NullstellenFunktion(double *Zeiger (double))
    

    Müsste dieser Teil aber nicht eigentlich dann

    void set_NullstellenFunktion(double *NullstellenFunktion (double));
    

    heißen?

    Das double waaahrscheinlich nicht die richtige Wahl ist, habe ich mir schon gedacht, aber das kann ich nach Bedarf ja ändern, wenn ich weiter bin und mehr weiß 🙂


  • Mod

    Mikomi schrieb:

    Welche Bezeichner meinst du damit?

    Die Regeln gelten auch für Makros.



  • SeppJ schrieb:

    Mikomi schrieb:

    Welche Bezeichner meinst du damit?

    Die Regeln gelten auch für Makros.

    Hmm, komisch, gerade bei den Makros habe ich das in dem Beispiel gesehen, aber nun gut. Lässt sich ja schnell ändern, danke für den Hinweis 🙂



  • Mikomi schrieb:

    SeppJ schrieb:

    Warum eine Nullstellensuche überhaupt eine Klasse sein sollte, kann dir wohl nur dein Lehrer erklären. Oder auch nicht. Denn es sollte keine Klasse sein.

    Die Klasse zu erstellen ist erst der Anfang meines Projekts. Im späteren Teil soll ich einmal mittels friend-Funktion und anhand eines Beispiels die Klasse testen, allerdings ist die Klasse richtig zu programmieren derzeit mein größtes Problem an dem Projekt :S

    Es sollte trotzdem keine Klasse sein. Der Lehrer tippt in C++ aber denkt in Java.



  • Allerdings gibt mir das Programm eine Fehlermeldung, wenn ich (double *fp)(double) hinschreibe, weil es mit den vielen Klammern irgendwie nicht klar kommt...

    Stimmt, sorry kleiner Tippfehler. 🙂

    Es sollte trotzdem keine Klasse sein. Der Lehrer tippt in C++ aber denkt in Java.

    Die Philosophie von C++ und speziell die damit verbundene Objektorientierung ist eine Sache, das Vermitteln der Funktionalität eine andere.
    Wenn es jetzt darum ginge darüber zu diskutieren, ob man für Nullstellen eine Klasse anlegen sollte würden du und SeppJ mir ordentlich in den Hintern treten. Nach dem Lesen der Aufgabenstellung habe ich mir auch erst mal an den Kopf gefasst, trotzdem lassen sich die gelernten Funktionalitäten letztendlich Problemlos auf andere Klassen übertragen und das ist es, was für Anfänger zählt.



  • Fernbedienung schrieb:

    trotzdem lassen sich die gelernten Funktionalitäten letztendlich Problemlos auf andere Klassen übertragen und das ist es, was für Anfänger zählt.

    Kochkurs:
    So, wir machen jetzt Spaghetti. Holt mal die Pfannen raus.

    Aber meine Mutter benutzt dazu einen Topf!

    Ja, aber das Gelernte könnt ihr später auf Schnitzel übertragen.

    Das funktioniert nicht.



  • Kochkurs:
    So, wir machen jetzt Spaghetti. Holt mal die Pfannen raus.

    Aber meine Mutter benutzt dazu einen Topf!

    Ja, aber das Gelernte könnt ihr später auf Schnitzel übertragen.

    Hier geht es erst mal darum den Herd zu bedienen mein Freund. Man kann dir noch so viele Töpfe und Pfannen um die Ohren hauen. Wenn du nicht weißt wie der Herd bedient wird musst du dir wohl weiterhin Hamburger in die Mikrowelle schieben.



  • Finde die Diskussion über die Aufgabenstellung sehr interessant 😃 Leider habe ich nicht die Wahl, ob ich es mit oder ohne Klasse programmiere, andernfalls wäre ich die Aufgabe auch anders angegangen 🙂

    Habe jetzt versucht die Vorschläge einzubauen, bin mir hier und da allerdings noch etwas unsicher, ob ich es richtig gemacht habe. Ist folgender Code besser?

    #ifndef NULLSTELLE_H
    #define NULLSTELLE_H
    class Nullstelle {
    
    	private:
    	double *NullstellenFunktion (double);                                //Zeiger auf Funktion f; f liegt in [a,b] und ist daher von Typ double
        double Untergrenze, Obergrenze;                                      //Intervallränder eines abgeschlossenen Intervalls [a,b]
        double epsilon;                                                      //Genauigkeitsschranke epsilon
        int i_max;                                                           //maximale Anzahl Iterationen i_max für Nullstellensuche
    
        public:
        	                                                                 //Deklaration des Konstruktors
        Nullstelle(double *NullstellenFunktion(double), double Untergrenze, double Obergrenze, double epsilon, int i_max);
    
        void set_NullstellenFunktion(double *NullstellenFunktion(double));   //geeignete Set- und Get- Methoden
        double get_Nullstellen();
        void set_Untergrenze(double a);
        double get_Untergrenze() const;
        void set_Obergrenze(double b);
        double get_Obergrenze() const;
    	void set_epsilon(double epsilon);  
        double get_epsilon() const;
    	void set_i_max(int i_max);
        int get_i_max()const;
    
    };
    
    #endif
    

  • Mod

    Deine Funktionszeiger sind keine.
    double *NullstellenFunktion(double) deklariert eine Funktion namens NullstellenFunktion mit double als Argument und double* als Rückgabewert.

    get_Nullstellen war nirgendwo verlangt. Und sollte wohl mindestens const sein.

    Außerdem ist es verwirrend: "Nullstellen"? Sind das nun eine oder mehrere? Wie gibst du mehrere zurück? Warum heißt die Klasse dann "Nullstelle"? Die Ursache dieser Probleme liegt natürlich darin, dass die Aufgabenstellung Mist ist; eine zufriedenstellende Lösung innerhalb der Vorgaben wird schwer zu finden sein.

    PS: Was, wenn es keine Nullstelle gibt?



  • Mach ein using und nenn die klasse um wenn du darfst:

    using Function = double(*)(double);
    
    class BoundedFunction
    {
        Function f;
        double lowerBound, upperBound, eps;
        unsigned int maxIter;
    
    public:
        BoundedFunction(Function f, double lowerBound, double upperBound, double eps, unsigned int maxIter);
        // Getter/Setter hier
    
        // Für beliebig viele Nullstellen kannst du z.B. std::vector nehmen
        // "find" ist da auch besser, weil nicht nur ein simpler Getter
        // "Roots" statt "Nullstellen" um nicht Deutsch und Englisch zu mischen
        std::vector<double> findRoots();
    };
    

    dann gibts weniger Logikprobleme und das lebeln wird leichter.



  • Es soll nur eine Nullstelle gefunden werden. Sorry, das war vermutlich etwas unverständlich formuliert.
    @SeppJ: Wie könnte ich den Funktionszeiger denn richtig formulieren?

    @Denglisch: Danke für den Vorschlag, leider ist bereits in den Nachfolgenden Teilen vorgegeben wie ich an die Nullstelle gelangen soll 🙂

    Der nächste Aufgabenteil ist auch ein wenig komplexer:
    Man ergänze die Klasse um eine Memberfunktion, die für eine gegebene Funktion f die Bedingung f(a) · f(b) < 0 überprüft und im positiven Fall eine Nullstelle nach folgender Vorschrift (Regula falsi) annähert:
    setze c = a - f(a)((a-b)/(f(a)-f(b)))
    setze i = 0
    wiederhole solange |b - a| > eps und f(c) /= 0 und i < imax
    falls (f(a) · f(c) < 0)
    ersetze b durch c
    sonst
    ersetze a durch c
    setze c = a - f(a)
    ((a-b)/(f(a)-f(b)))
    erhöhe i = i + 1
    Ist f(a) · f(b) = 0 so soll entweder a oder b als Nullstelle gefunden werden. Im Fall f(a) · f(b) > 0 soll die Berechnung ausgesetzt werden und eine entsprechende Meldung am Bildschirm ausgegeben werden (Das würde SeppJs Frage entsprechen was ist, wenn keine Nullstelle gefunden wird)

    Mein erster Ansatz (vermutlich voller Fehler) wäre:

    void Regula_Falsi(double epsilon, int i_max, double a, double b) {   //Regula Falsi Bedingung
        	int i = 0;                                                       //Starte mit i=0 = #Iterationen
        	double c; 
        	while (i < i_max, f(c)!= 0, abs(b-a) >= epsilon) {               // f:=NullstellenFunktion (später ändern)
        		if (f(a)*f(b) < 0) {
        			c = a - ((a - b)/(f(a) - f(b)))*f(a);
        		}
        		if (f(a)*f(c) < 0) {
        			b = c;                                         // Ersetze b durch c, a wie zuvor
        		}
        		else {
        			a = c;                                         // Ersetze a durch c, b wie zuvor (noch nicht sicher, ob es so funktioniert)
        		}
        		i++                                                // Setze i=i+1
    
        		if (f(a)*f(b) > 0)                                 // Fallunterscheidung, nicht sicher ob man das so in eine while Schleife bauen kann
        		cout << "Berechnung wird ausgesetzt" << endl;
        		if (f(a)*f(b) = 0) {                               // Bei =0 ist entweder a oder b Nullstelle von f
        			f(a) = 0 oder f(b)=0                           // Nicht sicher wie man dieses entweder-oder schreiben soll
        		}
    
        	}
        }
    

  • Mod

    Mikomi schrieb:

    @SeppJ: Wie könnte ich den Funktionszeiger denn richtig formulieren?

    double (*wie_auch_immer_das_ding_heißen_soll)(double);
    

    @Denglisch: Danke für den Vorschlag, leider ist bereits in den Nachfolgenden Teilen vorgegeben wie ich an die Nullstelle gelangen soll 🙂

    Schade, denn der Vorschlag war gut und ist eine der Möglichkeiten, der Aufgabenstellung noch einen Sinn zu entlocken. Aber wenn ich solche Anforderungen lese, bekomme ich eher das Würgen und bemitleide dich:

    Ist f(a) · f(b) = 0 so soll entweder a oder b als Nullstelle gefunden werden. Im Fall f(a) · f(b) > 0 soll die Berechnung ausgesetzt werden und eine entsprechende Meldung am Bildschirm ausgegeben werden (Das würde SeppJs Frage entsprechen was ist, wenn keine Nullstelle gefunden wird)



  • @SeppJ: Freut mich zumindest, dass ich nicht der einzige bin, dem diese Aufgabenstellung zuwider ist. Leider habe ich keine Wahl als der Aufgabenstellung zu folgen. Andere Lösungswege werden nicht akzeptiert.
    Und leider muss ich letzten Endes mit der vorgegebenen "Klassen"-Methode ein fehlerfreies Programm schreiben um den Einführungskurs abzuschließen...


Log in to reply