Навеяно постом. Попутно получилось что-то похожее на собственную реализацию лямбда-выражений :) С возможностью вычисления производной еще на этапе компиляции. Для задания функции можно использовать операторы +, -, *, /, а также ряд стандартных математических функций.
Sqr — возведение в квадрат
Sqrt — квадратный корень
Pow — возведение в действительную степень
Exp — показательная функция
Log — логарифм
Sin, Cos, Tg, Ctg, Asin, Acos, Atg, Actg — тригонометрия
Производная вычисляется с помощью функции derivative. На входе у нее — функтор, на выходе — тоже. Для того, чтобы производная была вычислена точно, на вход должен подаваться функтор, заданный с помощью особого синтаксиса. Синтаксис — интуитивно понятный (по крайней мере, я на это надеюсь). Если на вход derivative подать любой другой функтор или лямбду с подходящей сигнатурой (double -> double), то производная будет вычислена приближенно.
Пример:
Работает это так:
Файл CrazyMath.h получился достаточно большим, поэтому полностью включать его в статью смысла нет. Те, кому интересно, могут скачать исходники с Github'а
UPD Добавил в класс Derivative метод expression и typedef соответствующий типу, который возвращает метод expression. Метод expression возвращает функтор, пригодный для дальнейшего дифференцирования. Однако при вычислении производных 2-го, 3-го и более высоких порядков размер выражения быстро растет, поэтому компиляция может затянуться.
Sqr — возведение в квадрат
Sqrt — квадратный корень
Pow — возведение в действительную степень
Exp — показательная функция
Log — логарифм
Sin, Cos, Tg, Ctg, Asin, Acos, Atg, Actg — тригонометрия
Производная вычисляется с помощью функции derivative. На входе у нее — функтор, на выходе — тоже. Для того, чтобы производная была вычислена точно, на вход должен подаваться функтор, заданный с помощью особого синтаксиса. Синтаксис — интуитивно понятный (по крайней мере, я на это надеюсь). Если на вход derivative подать любой другой функтор или лямбду с подходящей сигнатурой (double -> double), то производная будет вычислена приближенно.
Пример:
#include <iostream>
#include "CrazyMath.h"
using namespace std;
using namespace CrazyMath;
auto global = Tg(X) + Ctg(X) + Asin(X) * Acos(X) - Atg(X) / Actg(X);
auto d_global = derivative(global);
int main()
{
auto f1 = (Pow(X, 3) + 2 * Sqr(X) - 4 * X + 1 / Sqrt(1 - Sqr(X))) * (Sin(X) + Cos(X) * (Log(5, X) - Exp(2, X)));
auto f2 = derivative(f1) * Sqrt(X - Tg(X / 4));
auto f3 = [](double x) -> double { return sin(x); };
auto df1 = derivative(f1);
auto df2 = derivative(f2);
auto df3 = derivative(f3);
cout << "f(x)\t\tf'(x)" << endl;
cout << f1(0.5) << " \t" << df1(0.5) << endl;
cout << f2(0.5) << " \t" << df2(0.5) << endl;
cout << f3(0) << " \t" << df3(0) << endl;
cout << global(0.5) << " \t" << d_global(0.5) << endl;
char temp[4];
cout << "\nPress ENTER to exit..." << endl;
cin.getline(temp, 3);
return 0;
}
Работает это так:
// CrazyMath.h, отрывок
//---------------------------------------------------
// base functions
class Const {
public:
typedef Const Type;
Const(double x) : m_const(x) {}
double operator()(double) {}
private:
double m_const;
};
class Simple {
public:
typedef Simple Type;
double operator()(double x)
{
return x;
}
};
template <class F1, class F2>
class Add {
public:
typedef typename Add<F1, F2> Type;
Add(const F1& f1, const F2& f2) : m_f1(f1), m_f2(f2) {}
double operator()(double x)
{
return m_f1(x) + m_f2(x);
}
F1 m_f1;
F2 m_f2;
};
//---------------------------------------------------
// helpers
template <class F1, class F2>
Add<F1, F2> operator+(const F1& f1, const F2& f2)
{
return Add<F1, F2>(f1, f2);
}
template <class F>
Add<F, Const> operator+(double value, const F& f)
{
return Add<F, Const>(f, Const(value));
}
template <class F>
Add<F, Const> operator+(const F& f, double value)
{
return Add<F, Const>(f, Const(value));
}
// other helpers ...
//---------------------------------------------------
// derivatives
template <class F>
class Derivative {
public:
Derivative(const F& f, double dx = 1e-3) : m_f(f), m_dx(dx) {}
double operator()(double x)
{
return (m_f(x + m_dx) - m_f(x)) / m_dx;
}
F m_f;
double m_dx;
typedef std::function<double (double)> Type;
Type expression()
{
return [this](double x) -> double
{
return (m_f(x + m_dx) - m_f(x)) / m_dx;
};
}
};
template<>
class Derivative<Const> {
public:
typedef Const Type;
Derivative<Const> (Const) {}
double operator()(double)
{
return 0;
}
Type expression()
{
return Const(0);
}
};
template<>
class Derivative<Simple> {
public:
typedef Const Type;
Derivative<Simple> (Simple) {}
double operator()(double)
{
return 1;
}
Type expression()
{
return Const(1);
}
};
template <class F1, class F2>
class Derivative< Add<F1, F2> > {
public:
Derivative< Add<F1, F2> > (const Add<F1, F2>& f)
: m_df1(f.m_f1), m_df2(f.m_f2)
{
}
double operator()(double x)
{
return m_df1(x) + m_df2(x);
}
Derivative<F1> m_df1;
Derivative<F2> m_df2;
typedef typename Add<typename Derivative<F1>::Type, typename Derivative<F2>::Type> Type;
Type expression()
{
return m_df1.expression() + m_df2.expression();
}
};
// other derivatives ...
template <class F>
typename Derivative<F>::Type derivative(F f)
{
return Derivative<F>(f).expression();
}
extern Simple X;
Файл CrazyMath.h получился достаточно большим, поэтому полностью включать его в статью смысла нет. Те, кому интересно, могут скачать исходники с Github'а
UPD Добавил в класс Derivative метод expression и typedef соответствующий типу, который возвращает метод expression. Метод expression возвращает функтор, пригодный для дальнейшего дифференцирования. Однако при вычислении производных 2-го, 3-го и более высоких порядков размер выражения быстро растет, поэтому компиляция может затянуться.