Frage zu "return"



  • Hallo allerseits,

    ich versuche mich gerade daran, ein paar numerische Algorithmen zu implementieren, um ein wenig Übung zu bekommen. Nun habe ich den folgenden Quellcode:

    void newt(double x_0)
    {
        double x_n = 0, y_test = fct(x_0);
    
        if(y_test < 1.e-30)
        {
            if(x_0 < 1.e30 && x_0 > -1.e30)
            {
                    x_0 = 0;
                    cout << "Nullstelle bei x = " << x_0 << endl;
            }
            else
                    cout << "Nullstelle bei x = " << x_0 << endl;
        }
        else
        {
            x_n = x_0 - fct(x_0)/der(x_0);
            newt(x_n);
        }
    }
    

    Wie man unschwer erkennen kann, handelt es sich hierbei um das Newton-Verfahren, mit dem man Nullstellen approximieren kann. Die Funktion an sich funktioniert in der Form schon sehr gut, jedoch dachte ich mir, dass ich flexibler arbeiten kann, wenn ich die Ausgaben etc. auf die main-Funktion auslagere. So habe ich die folgenden Veränderungen vorgenommen:

    double newt(double x_0) // Ausführen des Newtonverfahrens
    {
        double x_n = 0, y_test = fct(x_0);
    
        if(y_test < 1.e-30)
        {
            if(x_0 < 1.e30 && x_0 > -1.e30)
            {
                    x_0 = 0;
                    return 0;
            }
            else
            {
                    return x_0;
            }
        }
        else
        {
            x_n = x_0 - fct(x_0)/der(x_0);
            newt(x_n);
        }
    }
    

    und die main() entsprechend:

    int main(int argc, char** argv) {
    
        double erg = 0, x_0 = 4;
    
        erg = newt(x_0);
        cout << erg << endl;
    
        return 0;
    }
    

    Leider übergibt die Funktion einen falschen Wert. So habe ich, insofern ich mir den letzten Wert, der von der Funktion newt() berechnet wird, direkt von dieser Funktion aufs Terminal ausgeben lasse, die richtige Nullstelle als Ergebnis. Gebe ich mir die Variable "erg" in der main()-Funktion aus, erscheint nur "nan" in der Konsole, offenbar geht also etwas bei der Übergabe schief.

    Kann mir wer erklären, was da falsch ist?

    Viele Grüße



  • Was gibt die Funktion denn zurück, wenn der Test in Zeile 5 false ist? Suche mal nach rekursion. 😉



  • Hi!

    Whups, das habe ich glatt übersehen. Habe das jetzt weiter eingegrenzt, sodass nicht alles unter der x-Achse auch als Nullstelle gewertet wird. *g*

    Wie dem auch sei, leider gibt er mir bei der Übergabe an die main() weiterhin "nan" aus, während die direkte Ausgabe in newt() weiterhin das richtige Ergebnis liefert. :S

    Viele Grüße und danke für die Antwort!

    Edit: hab dein Edit nicht rechtzeitig gesehen, daher schiebe ich hier mein Edit nach: Gute Frage, wahrscheinlich "nichts", im wahrsten Sinne, denn für den Fall ist ja kein return eingetragen. Guter Punkt eigentlich.



  • Zeig doch noch mal deinen Code.
    Edit: Jetzt habe ich dein Edit nicht gesehen. 😃



  • Hehe.

    Ich frage mich gerade, was ich da als return eintragen kann, denn der Algorithmus durchläuft das Ding ja rekursiv und soll ja nicht nach jedem Rekusrionsschritt einen Wert zurückgeben, sondern erst dann, wenn die Nullstelle tatsächlich gefunden ist. Kann man dem irgendwie sagen "gib erstmal nichts zurück und warte, bis die NST gefunden ist"?

    Grüße



  • Zurückgegeben werden nicht Variablen, sondern Werte.. und zurückgegeben wird immer an den Aufrufer, das muss nicht zwangsläufig die main Funktion sein.. 💡
    (Einfach die Lösung sagen wäre ja langweilig. :p)



  • Heureka!

    double newt(double x_0) // Ausführen des Newtonverfahrens
    {
        double x_n = 0, y_test = fct(x_0), erg = 0;
    
        if(y_test < 1.e-30 && y_test > -1.e-30)
        {
            return x_0;
        }
        x_n = x_0 - fct(x_0)/der(x_0);
    
        return newt(x_n);
    }
    

    Scheint zu funktionieren!

    Vielen Dank für die Hinweise!

    Tante Edit: Grade noch nen Absolutbetrag in der Bedingung spendiert, das sieht hübscher aus. 🙂



  • Und da bin ich wieder. Diesmal mit einem, wahrscheinlich, noch trivialerem Problem. Ich scheine den Wald vor lauter Bäumen nicht zu sehen.

    Folgendes Problem: Um von Funktionen mehrere Nullstellen bestimmen zu können, ohne das Programm mehrfach ausführen zu müssen, habe ich eine while()-Schleife in die main()-Funktion eingebaut, die quasi kleine Teilintervalle auf Nullstellen überprüft. Dazu der folgende Quellcode:

    int main(int argc, char** argv)
    {
        double from = 0, to = 0, erg = 0, erg_1 = 1.e200, step = 0;
        ofstream ausgabe;
    
        /* Benutzereingaben */
        cout << "Parametereingabe:" << endl;
    
        cout << "Startwert: ";
        cin >> from;
        cout << "Endwert: ";
        cin >> to;
        cout << "Schrittweite: ";
        cin >> step;
    
        /* Ausführen des Algorithmus in kleinen Teilintervallen */
        while(from <= to)
        {     
            erg = newt(from); // Finden der Nullstellen einer Funktion
    
            if(erg != erg_1) // Nur neue Ergebnisse ausgeben
            {
                    cout << "Nullstelle bei x = " << erg << endl; // Ausgabe der Nullstelle auf das Terminal
    
                    /* Dateioperationen */
                    ausgabe.open("nullstellen.dat",ios::app); // Ausgabedatei öffnen
                    if(ausgabe.is_open()) // Nur schreiben, wenn die Datei geöffnet ist
                            ausgabe << erg << endl; // Nullstelle in Datei schreiben
                    else
                        cout << "Datei kann nicht aufgerufen werden!" << endl;
                    ausgabe.close(); // Ausgabedatei schließen
            }
            erg_1 = erg;
            from += step;
        }
    
        return 0;
    }
    

    Dummerweise scheint die Schleife abzubrechen, sobald eine Nullstelle gefunden wurde, woran kann das liegen? Meiner Ansicht nach ist die Exit-Bedingung doch eigentlich eindeutig.

    Viele Grüße!



  • Fehler liegt woanders, es wird nur eine Nullstelle gefunden. Der Code ist so korrekt. (Nicht unbedingt schön. :p)



  • Hmm, dann suche ich mal die restlichen Funktionen ab. Wie kann man denn, nur aus Interesse, schöner coden?



  • Kleiner Tipp:

    ich würde deine Funktion so umschreiben:

    double newt(double x_0, double(*fct)(double)) // Ausführen des Newtonverfahrens
    {
        double x_n = 0, y_test = fct(x_0), erg = 0;
    
        if(y_test < 1.e-30 && y_test > -1.e-30)
        {
            return x_0;
        }
        x_n = x_0 - fct(x_0)/der(x_0);
    
        return newt(x_n);
    }
    

    Der zweite Parameter ist, die Funktion, die untersucht werden soll.

    (Kleine Frage: was macht die Funktion der() ? Ist das die Ableitung? falls ja müsste die Funktion so aussehen:

    double newt(double x_0, double(*fct)(double), double(*der)(double))
    //...
    

    )

    Noch Fragen? Und glaub mir, so ist besser.

    Und aufruf sieht so aus:

    newt(x, &funktion);     // Mit zwei Parametern und "der()" ist eine feste Funktion
    newt(x, &funktion, &der);    //Mit drei Parametern und "der()" ist die Ableitung.
    

    Viel Spaß damit!



  • Die erinnerung schrieb:

    Noch Fragen? Und glaub mir, so ist besser.

    Ja: Warum kein Template?



  • Es geht hier um Funktionen.

    Natürlich könnte man noch ein Template reinhauen, aber das wäre, glaube ich, zu viel.

    Nagut, hier mit Template:

    //Eine Funktion
    template <class T> T newt(T x_0, T(*fct)(T))
    {
        //...
    }
    
    //Zwei Funktionen:
    template <class T> T newt(T x_0, T(*fct)(T), T(*der)(T))
    {
        //...
    }
    

    Würde auch gehen...



  • Die erinnerung schrieb:

    Es geht hier um Funktionen.

    Natürlich könnte man noch ein Template reinhauen, aber das wäre, glaube ich, zu viel.

    Nagut, hier mit Template:

    Würde auch gehen...

    rofl, nein.

    template <typename Functor>
    double newt(double x_0, Functor fct)
    {
        double x_n = 0, y_test = fct(x_0), erg = 0;
    
        if(y_test < 1.e-30 && y_test > -1.e-30)
        {
            return x_0;
        }
        x_n = x_0 - fct(x_0)/der(x_0);
    
        return newt(x_n, fct);
    }
    

    Nicht nur übersichtlicher, sondern erzeugt auch noch sehr wahrscheinlich deutlich schnelleren Code. 😉



  • Das habe ich noch nicht gelernt.



  • Danke für die Antworten, ich schau mir das mal in Ruhe an. 🙂
    Habe btw. herausgefunden, weshalb die Funktion nicht alle Nullstellen findet: Die numerische Berechnung der Ableitung zickt bei bestimmten Werten für "dx" ein wenig rum. Wenn man das variiert, funktioniert es.



  • Nur, um Cooky zu ärgern kommt jetzt die ultimative Variante der Funktion:

    template <typename T, typename Functor>
    T newt(T x_0, Functor fct, Functor der)
    {
        double x_n = 0, y_test = fct(x_0), erg = 0;
    
        if(y_test < 1.e-30 && y_test > -1.e-30)
        {
            return x_0;
        }
        x_n = x_0 - fct(x_0)/der(x_0);
    
        return newt(x_n);
    }
    


  • Die erinnerung schrieb:

    Nur, um Cooky zu ärgern kommt jetzt die ultimative Variante der Funktion:

    Die ist aber falsch. fct und der müssen ja nicht vom gleichen Typ sein.



  • sollten aber, weil der die Ableitung von fct ist...

    Also doch richtig!



  • Hat das auch einen Sinn oder ist das nur geistige Onanie? Ändere lieber

    y_test < 1.e-30 && y_test > -1.e-30
    

    in

    abs(y_test) < 1e-30
    

    und frage dich dann, ob diese Schranke wirklich sinnvoll ist bzw. ob man sie nicht in Abhängigkeit von T jeweils anders wählen sollte.


Anmelden zum Antworten