Regula Falsi



  • Hallo, liebe Community! Ich soll für ein Projekt in einer Klasse eine Memberfunktion mit Regula-Falsi Bedingung programmieren. Was gemacht werden muss, habe ich soweit bereits verstanden, allerdings weiß ich nicht wirklich wie ich den unterstrichenen Teil programmieren kann. Ist jemand so freundlich mir einen Tipp zu geben wie man das machen könnte?

    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

    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, fabs(b-a) >= epsilon) {               // f:=NullstellenFunktion
        		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;      f(b)=0;                         // Nicht sicher wie man dieses entweder-oder schreiben soll, besonders an dieser Stelle bräuchte ich Hilfe
        		}
    
        	}
        }
    

    Ach und eine Frage hätte ich noch: Im nächsten Aufgabenteil soll ich diese Regula-Falsi Funktion mittels einer Friend-Funktion lösen. Ich verstehe nicht ganz inwiefern man hier etwas "lösen" müsste? Hat jemand eine Idee was damit gemeint sein könnte?



  • Hallo Mikomi,

    Du armer Mensch. Welcher Unwissende hat Dir denn diese Aufgabe verpasst. Abgesehen von einer programmtechnischen Sackgassen - Regula Falsi ist keine Methode, sondern ein Algorithmus also eine Funktion - scheint er noch nie eine Aufgabe mit Regula Falsi gelöst zu haben.
    Es ist zur Bestimmung einer Näherung einer Nullstelle völlig irrelevant, ob die beiden Funktionswerte unterschiedliche Vorzeichen haben oder nicht. Das einzige Argument dafür wäre die Forderung, dass die gesuchte Nullstelle im Intervall [a,b] liegen MUSS. Ansonsten macht das wenig Sinn.

    Vielmehr führt das Vorgehen, immer den Stützpunkt mit dem anderen Vorzeichen zu wählen,

    Mikomi schrieb:

    falls (f(a) · f(c) < 0)
    ersetze b durch c
    sonst
    ersetze a durch c

    dazu, dass die Bedingung

    Mikomi schrieb:

    |b - a| > eps

    immer(!) erfüllt ist (also nie die Suche beendet), wenn der gegenüberliegende Stützpunkt weiter als 'epsilon' von der Nullstelle entfernt ist. Und letzteres ist i.A. immer gegeben. Probiere das mal mit einer einfachen Funktion aus - z.B. einer simplen nach oben geöffneter Parabel.

    Jede neu berechnete Näherung ist immer besser als die beiden Vorgänger. Also ist eine gleichbleibende Rotation geboten:

    a = b;
        b = c;
        // und weiter mit a und b rechnen
    

    Zu Deinem Code:
    - die Methode Regula_Falsi liefert kein Ergebnis zurück (Returntyp == void)
    - in Zeile 4 ist c nicht definiert, und f(c) demnach auch nicht
    - wenn f(a)*f(b) >= 0, ist in Zeile 8 c immer noch definiert; gleiches in Zeile 9 und 12
    - in Zeile 18 muss es == heißen.
    - Zeile 19 macht keinen Sinn (siehe dazu unten)

    Zu Deiner konkreten Frage, den Code könnte man in etwa so formulieren:

    if( f(a)*f(b) > 0 )
        { // beide Vorzeiche sind gleich
            throw std::runtime_error( "Berechnung regula falsi aussetzen" );
            // ... oder Du setzt ein Flag in einer zusätzlichen Ausgangsvariable und rufst 'return'
        }
        if( f(a)*f(b) == 0 )
        {  // f(a) oder f(b) == 0
            if( f(a) == 0 )
                return a; // erfordert einen Returntyp double
            return b;
        }
    

    Zu der Frage mit der friend Funktion: eine friend-Funktion macht genau dann Sinn, wenn etwas private ist, aber trotzdem der Zugriff möglich sein soll. Das sehe ich hier nicht.

    Noch ein Hinweis: es ist üblich die Berechnung einer Funktion an ein und derselben Stelle nur genau einmal auszuführen. Besser nur einmal

    double fa = f(a);
        double fb = f(b);
    

    aufrufen und von da an mit den Variablen fa und fb weiter machen.

    Gruß
    Werner


  • Mod

    Aus f(a)*f(b) == 0 folgt nicht zwangsläufig, dass einer der beiden Faktoren Null gleicht.



  • Arcoth schrieb:

    Aus f(a)*f(b) == 0 folgt nicht zwangsläufig, dass einer der beiden Faktoren Null gleicht.

    Nein?



  • TGGC schrieb:

    Arcoth schrieb:

    Aus f(a)*f(b) == 0 folgt nicht zwangsläufig, dass einer der beiden Faktoren Null gleicht.

    Nein?

    Ich vermute die Aussage ist in Bezug auf die double s gemeint, und nicht im mathematischen Sinne. Ansonsten würde mich ein Gegenbeispiel auch brennend interssieren 🙂

    Finnegan


  • Mod

    TGGC schrieb:

    Arcoth schrieb:

    Aus f(a)*f(b) == 0 folgt nicht zwangsläufig, dass einer der beiden Faktoren Null gleicht.

    Nein?

    Doch, du Schlaumeier. Warum habe ich wohl C++-Syntax verwendet? 🙄



  • Wow, brauchst ja nicht gleich ausflippen, nur weil du dich mal uneindeutig ausgedrueckt hast.


  • Mod

    TGGC schrieb:

    Wow, brauchst ja nicht gleich ausflippen, nur weil du dich mal uneindeutig ausgedrueckt hast.

    Ich habe mich IMO recht eindeutig ausgedrückt. Hätte ich von der entsprechenden mathematischen Gleichung gesprochen, hättest du f(a)f(b)=0f(a) \cdot f(b) = 0 gelesen. Ich formatiere i.d.R. konsequent. 🙂



  • Wenn du so wert auf genauen Ausdruck legst, dann ueberdenke auch dein "Doch". Das macht so naemlich keinen Sinn.



  • Werner Salomon schrieb:

    Hallo Mikomi,

    Du armer Mensch. Welcher Unwissende hat Dir denn diese Aufgabe verpasst.

    Danke für die Anteilnahme ^^' Ich versuch mein bestes die Aufgabe dennoch irgendwie zu lösen, doch für einen Anfänger ist das ganz schön schwer, daher großes Danke für die Hilfe 🙂

    Werner Salomon schrieb:

    Jede neu berechnete Näherung ist immer besser als die beiden Vorgänger. Also ist eine gleichbleibende Rotation geboten:

    a = b;
        b = c;
        // und weiter mit a und b rechnen
    

    Hmm, ich verstehe wo das Problem liegt und was gemeint ist, allerdings verstehe ich nicht ganz wie ich es richtig setzen soll...

    Das wäre jetzt nochmal ein Ansatz, allerdings vermute ich einige Fehler...

    double Regula_Falsi(double epsilon, int i_max, double a, double b) {        // Regula Falsi Bedingung als Friend-funktion
        	int i = 0;                                                                // Starte mit i=0 = #Iterationen
        	double c = a - ((a - b)/(f(a) - f(b)))*f(a); 
        	while (i < i_max, f(c)!= 0, fabs(b-a) >= epsilon) {                       // f:=NullstellenFunktion
        		if (f(a)*f(b) < 0) {
        			if (f(a)*f(c) < 0) {
        				b = a; a = c;                                                 // Ersetze b durch c
        			}
        			else {
        				a = b; b = c;                                                 // Ersetze a durch c
        			}
        			i++;                                                              // Setze i=i+1
        		}
        	}
    		if( f(a)*f(b) > 0) {
    			cout << "Berechnung wird ausgesetzt" << endl;
    		}
    		if (f(a)*f(b) == 0) {
    			if(f(a) == 0)
    			   return a;
    			else
    			return b;
    		}
        }
    

    Zu der "friend-Funktion"-Sache... Es steht explizit in der Aufgabenstellung ich solle die Regula-Falsi 'Methode' mittels friend-Funktion 'lösen', ich versteh auch nicht wirklich inwiefern ich hier irgendetwas mit friend-Funktion machen sollte 😕



  • Hallo Mikomi,

    bevor Du Dir überhaupt Gedanken über Deinen Code machst, solltest Du Dir überlegen, wie Du das Problem mit Papier und Bleistift (und meinetwegen noch mit Taschenrechner) lösen würdest.

    Zunächst musst Du doch von beiden Werten a und b die Funktionswerte bestimmen - und dann in jeden einzelnen Schritt genau einmal eine neue Näherung berechnen und anschließend - falls es noch nicht genau genug ist - beurteilen, welches Deine neuen Werte für den nächsten Schritt sind.
    Also in Code:

    double fa = f(a);
        double fb = f(b);
        // jetzt evt. prüfen ob fa*fb <= 0 sind
        while/for(;;) { // eine Schleife; Details später
            double c = ... ; // nächsten Näherung bestimmen
            double fc = f(c);
            // jetzt entscheiden, ob fc schon ok ist, dann raus mit return; c ist die Lösung
    
            // sonst ermitteln welches die 'besten' oder lt. Aufgabenstellung geforderten Werte für a und b und deren Funktionswerte sind:
            a = .. ;
            fa = .. ;
            b = .. ;
            fb = ... ;
        }
    

    .. fertig. Das war's schon - oder?

    Jetzt noch überlegen, was den Aufrufer von 'Regula_Falsi' noch interessiert. Es fehlt noch die Information, ob das ganze gut gegangen ist. Dafür führe ich einen weiteren Parameter ein:

    double Regula_Falsi(double epsilon, int i_max, double a, double b, bool& ok)
    

    'ok' soll den Wert 'true' annehmen, wenn der Returnwert gültig ist, also eine gute Näherung gefunden wurde.

    Auch wenn ich es unsinnig finde, gehen ich jetzt mal streng nach Aufgabestellung vor. Der konkreten Code:

    #include <iostream>
    #include <cmath> // wg. std::abs
    
    using namespace std;
    
    class Egal
    {
    public:
        double f( double x ) // eine simple Parabel als Beispiel
        {
            return (x - 6)* x + 8;  // Nullstellen: x_1 = 2; x_2 = 4
        }
        double Regula_Falsi(double epsilon, int i_max, double a, double b, bool& ok) {
            double fa = f(a);
            double fb = f(b);
            if( fa * fb <= 0. )   // ==0 wäre auch ok, weil dann evt. a oder b bereits die Lösung beinhaltet!
            { // "... die für eine gegebene Funktion f die Bedingung f(a) · f(b) < 0 überprüft "
                for( int i = 0; i < i_max; ++i ) // "setze i = 0  ...; wiederhole solange i < imax ; erhöhe i = i + 1 "
                {
                    double c = a - fa*(a-b)/(fa-fb); // "setze c = a - f(a)*((a-b)/(f(a)-f(b))) "
                    double fc = f(c);
                    // "wiederhole solange |b - a| > eps && f(c) != 0"
                    if( !(std::abs(b - a) > epsilon && fc != 0.) )    // Bem.: |b - a| > epsilon ist i.A immer erfüllt !!!
                    {
                        ok = true;
                        return c;
                    }
                    if( fa*fc < 0. )
                    {   // "falls (f(a) · f(c) < 0) "
                        b = c; // "ersetze b durch c "
                        fb = fc;
                    }
                    else // "sonst .. "
                    {   // "ersetze a durch c  "
                        a = c;
                        fa = fc;
                    }
                }
                // wenn Du hier aus der Schleife fällst, ist i >= i_max und keine gültige Näherung gefunden
            }
            ok = false; // Fehler - kein Ergebnis
            return 0.;
        }
    };
    
    int main()
    {
        Egal egal;
        bool ok;
        double ergebnis = egal.Regula_Falsi( 1.E-6, 20, 3., 10., ok );
        if( ok )
            cout << "Ergebnis = " << ergebnis << endl;
        else
            cout << "kein Ergebnis gefunden" << endl;
        return 0;
    }
    

    Alles was in "-Zeichen steht, stammt aus Deiner Aufgabestellung. Interessant ist, dass dieses Programm als Ergebnis "kein Ergebnis gefunden" ausgibt. Erhöhst Du die Anzahl der Schritte von 20 auf 200, so findet es die Nullstelle x=4. Die Gründe dafür habe ich in meinem letzten Posting schon beschrieben.

    Ersetze jetzt die Zeilen 28 bis 37 durch das einfache

    a = b;
                    fa = fb;
                    b = c;
                    fb = fc;
    

    dann findet der Algorithmus nach 10 Schritte bereits die Lösung. Die Abfrage in Zeile 16 ist dann genauso überflüssig.

    Zum Schluss noch 'ne Bemerkung zum Grübeln: Du kannst die Zeilen 28 bis 37 auch so lassen wie in der Aufgabestellung gefordert, wenn Du sie um den Algorithmus von Illinois, Pegasus oder besser Anderson/Björck erweiterst. Näheres findest Du hier: https://de.wikipedia.org/wiki/Regula_falsi
    So kämst Du auch in weniger als 20 Schritten zum Ziel.

    Gruß
    Werner

    @Edit: #include <cmath> hinzugefügt



  • Werner Salomon schrieb:

    Hallo Mikomi

    Hallo Werner, vielen vielen Dank für diese unglaublich ausführliche Antwort!
    Das hilft mir in Sachen Verständnis wirklich weiter! 🙂 👍

    Je mehr ich verstehe, desto weniger mag ich meine Aufgabenstellung allerdings...

    Mehr Hilfe kann ich an dieser Stelle vermutlich gar nicht mehr verlangen, ich sollte jetzt mit meinem Wissen und eurer aller Hilfe eigentlich in der Lage sein mein Projekt einigermaßen abschließen zu können.
    Vielen Dank für all die Tipps, Beispiele und Hinweise 🙂



  • Alsooo... Leider versagt dieser Code der Regula-Falsi Funktion bei komplexeren Funktionen. Allerdings konnte ich mithilfe der vielen Tipps und Hinweise, besonders von Werner Salomon, vielen Dank noch einmal, die Aufgabe gut genug lösen, dass ich meinen Kurs bestehen konnte.

    Liebe Community, vielen Dank für die Hilfe auf dem Weg dorthin! 🙂
    Dieser Thread kann hiermit geschlossen werden (Falls man das in diesem Forum so macht)

    Grüße,
    Mikomi


Log in to reply