Derivate in C/C++?

Lesezeit: 7 Minuten

Benutzer-Avatar
alanc10n

Ich habe einige Ausdrücke wie z x^2+y^2 die ich für einige mathematische Berechnungen verwenden möchte. Eines der Dinge, die ich gerne tun würde, ist partielle Ableitungen der Ausdrücke zu nehmen.

Also wenn f(x,y) = x^2 + y^2 dann der Teil von f in Gedenken an x wäre 2xdie teilweise in Bezug auf y wäre 2y.

Ich habe eine dinky Funktion mit einer Finite-Differenzen-Methode geschrieben, aber ich stoße auf viele Probleme mit der Gleitkommagenauigkeit. Zum Beispiel lande ich bei 1.99234 Anstatt von 2. Gibt es Bibliotheken, die symbolische Differenzierung unterstützen? Irgendwelche anderen Vorschläge?

  • Selber rollen wäre eine schlechte Idee. Ich habe einen übergreifenden Graduiertenkurs in Computeralgebra belegt, und die Dinge sind noch nicht einmal ganz vollständig. Eine vereinfachte Differenzierung könnte durch Anwendung maty-Regeln erfolgen und dann versuchen, sie zu ersetzen und auszuwerten … in C/C++ schwer zu bewerkstelligen.

    – Calyth

    8. Januar 2009 um 0:42 Uhr

  • Siehe auch stackoverflow.com/questions/627055/… und markieren Sie vielleicht Ihre Frageableitung

    – Daniel Daranas

    22. Juli 2009 um 7:47 Uhr

Ich habe solche Bibliotheken in mehreren verschiedenen Sprachen implementiert, aber leider nicht in C. Wenn Sie nur mit Polynomen (Summen, Produkte, Variablen, Konstanten und Potenzen) zu tun haben, ist es ziemlich einfach. Trig-Funktionen sind auch nicht schlecht. Alles, was komplizierter ist, und Sie werden wahrscheinlich besser dran sein, sich die Zeit zu nehmen, die Bibliothek eines anderen zu meistern.

Wenn Sie sich entscheiden, Ihre eigene Rolle zu drehen, habe ich ein paar Vorschläge, die Ihr Leben vereinfachen werden:

  • Verwenden Sie unveränderliche Datenstrukturen (rein funktionale Datenstrukturen), um Ausdrücke darzustellen.

  • Verwenden Der Müllsammler von Hans Böhm um den Speicher für Sie zu verwalten.

  • Um eine lineare Summe darzustellen, verwenden Sie eine endliche Abbildung (z. B. einen binären Suchbaum), um jede Variable ihrem Koeffizienten zuzuordnen.

Wenn Sie bereit sind, einzubetten Lua in Ihren C-Code und führen Sie dort Ihre Berechnungen durch, ich habe meinen Lua-Code unter eingefügt http://www.cs.tufts.edu/~nr/drop/lua. Eines der netteren Features ist, dass es einen symbolischen Ausdruck nehmen, ihn differenzieren und die Ergebnisse in Lua kompilieren kann. Sie werden natürlich keinerlei Dokumentation finden 🙁

Wenn Sie eine numerische Differenzierung durchführen (“bewerten Sie die Ableitung von f(x) bei x = x0“) und Sie wissen, dass Sie Gleichungen im Voraus sind (dh keine Benutzereingaben), dann würde ich empfehlen FADBAD++. Es ist eine C++-Vorlagenbibliothek zum Lösen numerischer Ableitungen Automatische Differenzierung. Es ist sehr schnell und genau.

  • Vielen Dank für den Vorschlag dieser Bibliothek. Ich habe es doppelt überprüft und eine Antwort für das OP gepostet. Es scheint eine sehr gute Bibliothek zu sein. Danke noch einmal.

    – CroCo

    2. Dezember 2015 um 8:37 Uhr


Die numerische Differenzierung “richtig” (im Sinne der Fehlerminimierung) hinzubekommen, kann ziemlich schwierig sein. Um zu beginnen, möchten Sie vielleicht einen Blick auf den Abschnitt Numerische Rezepte werfen numerische Ableitungen.

Für kostenlose symbolische Mathematikpakete sollten Sie sich umsehen GiNaC. Du könntest auch auschecken SymPy, ein in sich geschlossenes, reines Python-Paket für symbolische Mathematik. Sie werden feststellen, dass SymPy viel einfacher zu erkunden ist, da Sie es interaktiv über die Python-Befehlszeile verwenden können.

Auf der kommerziellen Seite haben sowohl Mathematica als auch Maple C-APIs. Sie benötigen eine installierte/lizenzierte Version des Programms, um die Bibliotheken zu verwenden, aber beide können problemlos die Art der symbolischen Differenzierung durchführen, nach der Sie suchen.

Benutzer-Avatar
Jan

Du kannst die Genauigkeit deiner numerischen Differentiation auf zwei einfache Arten verbessern

  1. Verwenden Sie ein kleineres Delta. Sie scheinen einen Wert von ungefähr verwendet zu haben 1e-2. Beginnen mit 1e-8, und testen Sie, ob es weh tut oder hilft, kleinere zu bekommen. Offensichtlich kann man der Maschinenpräzision nicht zu nahe kommen – ungefähr 1e-16 für doppelt.

  2. Verwenden Sie zentrale Unterschiede anstelle von Vorwärts- (oder Rückwärts-) Unterschieden. dh df_dx =(f(x+delta) - f(x-delta)) / (2.0*delta)
    Aus Gründen, die mit der Streichung höherer Verkürzungsterme zu tun haben, liegt der Fehler in der Schätzung der zentralen Differenzen in der Größenordnung delta^2 eher als das Delta der Vorwärtsdifferenzen. Sehen http://en.wikipedia.org/wiki/Finite_difference

Wenn dies wirklich die Art von Funktion ist, die Sie verwenden möchten, wäre es einfach genug, eine Klassenbibliothek zu schreiben. Beginnen Sie mit einem einzelnen Term, mit einem Koeffizienten und einem Exponenten. Haben Sie ein Polynom, das aus einer Sammlung von Termen bestehen würde.

Wenn Sie eine Schnittstelle für die interessierenden mathematischen Methoden definieren (z. B. add/sub/mul/div/differentiate/integrate), sehen Sie sich ein GoF-Composite-Muster an. Sowohl Term als auch Polynomial würden diese Schnittstelle implementieren. Das Polynom würde einfach über jeden Term in seiner Sammlung iterieren.

Benutzer-Avatar
j_random_hacker

Es wäre sicherlich einfacher, ein vorhandenes Paket zu nutzen, als ein eigenes zu schreiben, aber wenn Sie entschlossen sind, Ihr eigenes zu schreiben, und bereit sind, einige Zeit damit zu verbringen, einige dunkle Ecken von C++ zu lernen, können Sie das verwenden Boost.Proto aus Schub um Ihre eigene Bibliothek zu entwickeln.

Grundsätzlich erlaubt Ihnen Boost.Proto, jeden gültigen C++-Ausdruck zu konvertieren, wie z x * x + y * y zu einem Ausdrucksvorlage — im Grunde eine Darstellung des Parse-Baums dieses Ausdrucks unter Verwendung von nested structs — und dann zu einem späteren Zeitpunkt beliebige Berechnungen über diesen Analysebaum durch Aufrufen durchführen proto::eval() darauf. Standardmäßig, proto::eval() wird verwendet, um den Baum so auszuwerten, als ob er direkt ausgeführt worden wäre, obwohl es keinen Grund gibt, warum Sie das Verhalten jeder Funktion oder jedes Operators nicht ändern könnten, um stattdessen eine symbolische Ableitung zu nehmen.

Obwohl dies eine wäre äußerst komplexe Lösung für Ihr Problem, wäre es dennoch viel einfacher, als zu versuchen, Ihre eigenen Ausdrucksvorlagen mithilfe von C++-Vorlagen-Metaprogrammierungstechniken zu erstellen.

Benutzer-Avatar
CroCo

Entschuldigung, dass ich das nach 6 Jahren anspreche. Ich habe jedoch nach einer solchen Bibliothek für mein Projekt gesucht und dabei @eduffy vorgeschlagen FADBAD++. Ich habe die Dokumentation gelesen und bin auf Ihre Frage zurückgekommen. Ich denke, meine Antwort wird von Vorteil sein, daher ist der folgende Code für Ihren Fall.

#include <iostream>
#include "fadiff.h"

using namespace fadbad;

F<double> func(const F<double>& x, const F<double>& y)
{
    return x*x + y*y;
}

int main()
{
    F<double> x,y,f;     // Declare variables x,y,f
    x=1;                 // Initialize variable x
    x.diff(0,2);         // Differentiate with respect to x (index 0 of 2)
    y=1;                 // Initialize variable y
    y.diff(1,2);         // Differentiate with respect to y (index 1 of 2)
    f=func(x,y);         // Evaluate function and derivatives

    double fval=f.x();   // Value of function
    double dfdx=f.d(0);  // Value of df/dx (index 0 of 2)
    double dfdy=f.d(1);  // Value of df/dy (index 1 of 2)

    std::cout << "    f(x,y) = " << fval << std::endl;
    std::cout << "df/dx(x,y) = " << dfdx << std::endl;
    std::cout << "df/dy(x,y) = " << dfdy << std::endl;

    return 0;
}

Die Ausgabe ist

    f(x,y) = 2
df/dx(x,y) = 2
df/dy(x,y) = 2

Ein anderes Beispiel, sagen wir, dass wir an der ersten Ableitung von interessiert sind sin(). Analytisch schon cos. Das ist großartig, weil wir die wahre Ableitung einer gegebenen Funktion und ihr numerisches Gegenstück vergleichen müssen, um den wahren Fehler zu berechnen.

#include <iostream>
#include "fadiff.h"

using namespace fadbad;

F<double> func(const F<double>& x)
{
    return sin(x);
}



int main()
{
    F<double> f,x;
    double dfdx;
    x = 0.0;
    x.diff(0,1);
    f = func(x);
    dfdx=f.d(0);


    for (int i(0); i < 8; ++i ){
        std::cout << "       x: " << x.val()        << "\n"
                  << "    f(x): " << f.x()          << "\n" 
                  << " fadDfdx: " << dfdx           << "\n"
                  << "trueDfdx: " << cos(x.val())   << std::endl;
        std::cout << "=========================="   << std::endl;

        x += 0.1;
        f = func(x);
        dfdx=f.d(0);
    }


    return 0;
}

Das Ergebnis ist

       x: 0
    f(x): 0
 fadDfdx: 1
trueDfdx: 1
==========================
       x: 0.1
    f(x): 0.0998334
 fadDfdx: 0.995004
trueDfdx: 0.995004
==========================
       x: 0.2
    f(x): 0.198669
 fadDfdx: 0.980067
trueDfdx: 0.980067
==========================
       x: 0.3
    f(x): 0.29552
 fadDfdx: 0.955336
trueDfdx: 0.955336
==========================
       x: 0.4
    f(x): 0.389418
 fadDfdx: 0.921061
trueDfdx: 0.921061
==========================
       x: 0.5
    f(x): 0.479426
 fadDfdx: 0.877583
trueDfdx: 0.877583
==========================
       x: 0.6
    f(x): 0.564642
 fadDfdx: 0.825336
trueDfdx: 0.825336
==========================
       x: 0.7
    f(x): 0.644218
 fadDfdx: 0.764842
trueDfdx: 0.764842
==========================

1273940cookie-checkDerivate in C/C++?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy