C/C++ 中的导数?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/422594/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 15:16:01  来源:igfitidea点击:

Derivatives in C/C++?

c++cmathnumericalsymbolic-math

提问by alanc10n

I have some expressions such as x^2+y^2that I'd like to use for some math calculations. One of the things I'd like to do is to take partial derivatives of the expressions.

我有一些表达式,例如x^2+y^2我想用于一些数学计算的表达式。我想做的一件事是取表达式的偏导数。

So if f(x,y) = x^2 + y^2then the partial of fwith respect to xwould be 2x, the partial with respect to ywould be 2y.

所以,如果f(x,y) = x^2 + y^2再偏的f相对于x2x,部分相对于y2y

I wrote a dinky function using a finite differences method but I'm running into lots of problems with floating point precision. For example, I end up with 1.99234instead of 2. Are there any libraries that support symbolic differentiation? Any other suggestions?

我使用有限差分方法编写了一个极小的函数,但是我遇到了很多浮点精度问题。例如,我最终得到了1.99234而不是2. 是否有任何支持符号微分的库?还有其他建议吗?

回答by Norman Ramsey

I've implemented such libraries in several different languages, but unfortunately not C. If you are dealing only with polynomials (sums, products, variables, constants, and powers) it is pretty easy. Trig functions are also not too bad. Anything more complicated and you will probably be better off taking the time to master somebody else's library.

我已经用几种不同的语言实现了这样的库,但不幸的是不是 C。如果你只处理多项式(和、乘积、变量、常数和幂),这很容易。触发功能也不错。任何更复杂的事情,你可能最好花时间掌握其他人的图书馆。

If you decide to roll your own, I have a few suggestions that will simplify your life:

如果您决定自己动手,我有一些建议可以简化您的生活:

  • Use immutable data structures (purely functional data structures) to represent expressions.

  • Use Hans Boehm's garbage collectorto manage the memory for you.

  • To represent a linear sum, use a finite map (e.g., a binary search tree) to map each variable to its coefficient.

  • 使用不可变数据结构(纯函数式数据结构)来表示表达式。

  • 使用Hans Boehm 的垃圾收集器为您管理内存。

  • 要表示线性和,请使用有限映射(例如,二叉搜索树)将每个变量映射到其系数。

If you're willing to embed Luainto your C code and do your computations there, I have put my Lua code at http://www.cs.tufts.edu/~nr/drop/lua. One of the nicer features is that it can take a symbolic expression, differentiate it, and compile the results into Lua. You will of course find no documentation whatever :-(

如果您愿意将Lua嵌入您的 C 代码并在那里进行计算,我已将我的 Lua 代码放在http://www.cs.tufts.edu/~nr/drop/lua。更好的功能之一是它可以采用符号表达式,对其进行微分,并将结果编译到 Lua 中。你当然会发现没有任何文件:-(

回答by Barry Wark

Getting numerical differentiation "right" (in the sense of minimizing errors) can be quite tricky. To get started, you may want to take a look at the Numerical Recipies' section on numerical derivatives.

使数值微分“正确”(在最小化错误的意义上)可能非常棘手。要开始使用,您可能需要查看有关数值导数的 Numerical Recipies 部分。

For free symbolic math packages, you should look at GiNaC. You could also check out SymPy, a self-contained, pure-python symbolic math package. You will find that SymPy is much easier to explore, since you can use it interactively from the Python command line.

对于免费的符号数学包,您应该查看GiNaC。您还可以查看SymPy,这是一个自包含的纯 Python 符号数学包。您会发现 SymPy 更易于探索,因为您可以从 Python 命令行以交互方式使用它。

On the commercial end, both Mathematica and Maple have C APIs. You need an installed/licensed version of the program to use the libraries, but both can easily do the type of symbolic differentiation you're after.

在商业方面,Mathematica 和 Maple 都有 C API。您需要安装/许可的程序版本才能使用这些库,但两者都可以轻松完成您所追求的符号区分类型。

回答by eduffy

If you're doing numerical differentiation ("evaluate the derivative of f(x)at x= x0") and you know you're equations in advance (ie, not user input), then I'd recommend FADBAD++. It's a C++ template library for solving numeric derivatives using Automatic differentiation. It's very quick, and accurate.

如果您正在进行数值微分(“在x= x0处评估f(x)的导数”)并且您事先知道您是方程(即,不是用户输入),那么我会推荐FADBAD++。它是一个 C++ 模板库,用于使用自动微分求解数值导数。它非常快速、准确。

回答by Ian

You can improve the accuracy of your numerical differentiation in two simple ways

您可以通过两种简单的方式提高数值微分的准确性

  1. Use a smaller delta. You appear to have used a value of around 1e-2. Start with 1e-8, and test if getting any smaller hurts or helps. Obviously you can't get too close to the machine precision - about 1e-16for double.

  2. Use central differences rather than forward (or backwards) differences. i.e. df_dx =(f(x+delta) - f(x-delta)) / (2.0*delta)For reasons to do with cancellation of higher truncation terms, the error in the central differences estimate is of the order delta^2rather than the delta of forward differences. See http://en.wikipedia.org/wiki/Finite_difference

  1. 使用较小的增量。您似乎使用了 around 值1e-2。从1e-8,开始测试是否有任何较小的伤害或帮助。显然你不能太接近机器精度——大约1e-16是两倍。

  2. 使用中心差异而不是向前(或向后)差异。即,df_dx =(f(x+delta) - f(x-delta)) / (2.0*delta)由于取消较高截断项的原因,中心差异估计中的误差是顺序delta^2而不是远期差异的增量。见http://en.wikipedia.org/wiki/Finite_difference

回答by duffymo

If this really is the kind of function you want to use, it'd be easy enough to write a class library. Start with a single Term, with a coefficient and an exponent. Have a Polynomial that would consist of a Collection of Terms.

如果这确实是您想要使用的那种函数,那么编写一个类库就足够容易了。从单个 Term、一个系数和一个指数开始。有一个由一组项组成的多项式。

If you define an interface for the mathematical methods of interest (e.g., add/sub/mul/div/differentiate/integrate), you're looking at a GoF Composite pattern. Both Term and Polynomial would implement that interface. The Polynomial would simply iterate over each Term in its Collection.

如果您为感兴趣的数学方法定义了一个接口(例如,add/sub/mul/div/differentiate/integrate),您正在查看 GoF Composite 模式。Term 和 Polynomial 都将实现该接口。多项式将简单地迭代其集合中的每个 Term。

回答by j_random_hacker

It would certainly be easier to leverage an existing package than to write your own, but if you're determined to write your own, and you are prepared to spend some time learning about some dark corners of C++, you can use the Boost.Protofrom Boostto engineer your own library.

利用现有包肯定比编写自己的包更容易,但是如果您决心编写自己的包,并且准备花一些时间学习 C++ 的一些黑暗角落,则可以使用Boost.ProtoBoost设计您自己的库。

Basically, Boost.Proto allows you to convert any valid C++ expression, such as x * x + y * yto an expression template-- basically a representation of the parse tree of that expression using nested structs -- and then perform any arbitrary computation over that parse tree at a later time by calling proto::eval()on it. By default, proto::eval()is used to evaluate the tree as though it had been run directly, though there's no reason why you couldn't modify the behaviour of each function or operator to take a symbolic derivative instead.

基本上,Boost.Proto 允许您转换任何有效的 C++ 表达式,例如x * x + y * y转换为表达式模板——基本上是使用嵌套structs 的表达式解析树的表示——然后稍后对该解析树执行任何任意计算时间通过调用proto::eval()它。默认情况下,proto::eval()用于评估树,就好像它已经直接运行一样,尽管没有理由不能修改每个函数或运算符的行为以采用符号导数。

Although this would be an extremelycomplex solution to your problem, it would nevertheless be much easier than trying to roll your own expression templates using C++ template metaprogramming techniques.

尽管这对于您的问题来说是一个极其复杂的解决方案,但它比尝试使用 C++ 模板元编程技术来推出您自己的表达式模板要容易得多。

回答by CroCo

Sorry for bringing this up after 6 years. However, I was looking for such a library to my project and I've seen @eduffy suggests FADBAD++. I've read the documentation and came back to your question. I feel my answer will be beneficial, therefore, the following code is for your case.

很抱歉在 6 年后提出这个问题。但是,我正在为我的项目寻找这样的库,并且我看到 @eduffy 建议使用FADBAD++。我已阅读文档并回到您的问题。我觉得我的回答会有所帮助,因此,以下代码适用于您的情况。

#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;
}

The output is

输出是

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

Another example, let's say that we are interested in the first derivative of sin(). Analytically, it is cos. This is great because we need to compare the true derivative of a given function and its numerical counterpart to compute the true error.

另一个例子,假设我们对 的一阶导数感兴趣sin()。从分析上看,是cos。这很好,因为我们需要比较给定函数的真实导数与其数值对应物来计算真实误差。

#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;
}

The result is

结果是

       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
==========================

回答by Martin Kosicky

I created such a library in C++, you can see the sample here: https://github.com/MartinKosicky/auto_diff/blob/master/sample/main.cpp

我在 C++ 中创建了这样一个库,您可以在此处查看示例:https: //github.com/MartinKosicky/auto_diff/blob/master/sample/main.cpp

The usage is quite simple, it also supports matrices. I used it to create Recurrent Neural Network... I would need to add GPU matrix multiplication though

用法很简单,它还支持矩阵。我用它来创建循环神经网络...虽然我需要添加 GPU 矩阵乘法

回答by nlucaroni

This is kind of an aside since it applies to Lisp and not C/C++, but it could help others looking for similar tasks or you might get some ideas on implementing something similar in C/C++ on your own. SICP has some lectures on the subject for lisp:

这是一种旁白,因为它适用于 Lisp 而不是 C/C++,但它可以帮助其他人寻找类似的任务,或者你可能会得到一些关于在 C/C++ 中自己实现类似的东西的想法。SICP 有一些关于 lisp 主题的讲座:

  1. derivative rules 3b
  2. algebraic rules 4a
  1. 衍生规则3b
  2. 代数规则4a

In Lisp, it's pretty straight forward (and in other functional languages with powerful pattern matching and polymorphic types). In C, you would probably have to heavily utilize enums and structs to get the same power (not to mention allocation/deallocation). One could definitly code what you need in ocaml in under an hour --I'd say typing speed is the limiting factor. If you need C, you can actually call ocaml from C (and vice versa).

在 Lisp 中,它非常简单(在其他具有强大模式匹配和多态类型的函数式语言中)。在 C 中,您可能不得不大量使用枚举和结构来获得相同的功能(更不用说分配/解除分配)。人们可以在一小时内在 ocaml 中明确地编写出您需要的代码——我认为打字速度是限制因素。如果您需要 C,您实际上可以从 C 调用 ocaml(反之亦然)。

回答by Andrei Pokrovsky

Have a look at Theano, it supports symbolic differentiation (in the context of Neural Nets). The project is open source though so you should be able to see how they do it.

看看 Theano,它支持符号微分(在神经网络的背景下)。该项目是开源的,因此您应该能够看到他们是如何做到的。