2

多変数多項式のコンパイル時クラスを作成しようとしています (つまり、P(X,Y,Z) = X^2 + XYZ + YZ のように、ここでは数学についてあまり心配しないでください):

template<int DIM, int DEGREE> class Polynomial {
  public:
    constexpr Polynomial(std::array<double,nbOfCoeffs(DIM,DEGREE)> arr): coeffs(arr) {} 


    constexpr double eval(std::array<double,DIM> x);
    constexpr operator+,-,*,/ ...
  private:
    std::array<double,nbOfCoeffs(DIM,DEGREE)> coeffs; //don't worry about "nbOfCoeffs" : it is constexpr and computes at compile time the right number of coefficients. 
}

int main () {
    Polynomial<2,2> P({{1.,1.,1.,1.,1.,1.}}); // P(X,Y) = X^2+XY+Y^2+X+Y+1

    double x = P.eval(1.);
    auto P2 = P*P;
}

これまでのところ、これを実装するのに大きな問題はありません。ただし、私の ctor は少し面倒な場合があることに注意してください。3 次三変数多項式 P(X,Y,Z) = XYZ を初期化するにはどうすればよいですか? 私は次のようなものを持っているでしょう

Polynomial<3,3> P({{0.,0.,0.,0.,0.,1.,0.,0.,0.,0.}});

保存する場所に応じて、ゼロ以外の単項式のみの位置を使用します。私がちょうど書くことができればいいでしょう:

  Polynomial<3,3> P("XYZ"); 

  Polynomial<4,3> P("X1X2X3 + X4^2"); //more general 

アイデアは、多項式の文字列表現を処理するために、ある種の小さな DST を作成することです。

ただし、これを行うと、文字列が解析されると、格納している配列の要素に値を割り当てる方法がわかりません。ctor の本体を空のままにしておく必要があります (constexpr にしたいため)。どう思いますか ?出来ますか ?配列をある種の繰り返し構造に変更する必要があります (この場合、非常に複雑になると思うため)

4

2 に答える 2

3

Luc Dantonのアプローチを実装する方法の例:

#include <cstddef>
#include <iostream>

namespace polynomials
{
    // it's possible to store the exponent as data member instead
    template < std::size_t t_id, std::size_t t_exponent = 1 >
    struct monomial
    {
        static constexpr std::size_t id = t_id;
        static constexpr std::size_t exponent = t_exponent;

        // it's not possible to store the coefficient
        // as non-type template parameter (floating-point..)
        double coefficient;

        explicit constexpr monomial(double p_coefficient = 1.0)
            : coefficient{ p_coefficient }
        {}

        void print() const
        {
            std::cout << coefficient << "X" << t_id << "^" << t_exponent;
        }
    };

    // create the monomial objects (like std::placeholders::_1)
    constexpr monomial<0> X;
    constexpr monomial<1> Y;
    constexpr monomial<2> Z;

    constexpr monomial<4> X0;
    constexpr monomial<5> X1;
    // ... can use macros to produce a lot of them..

    // multiply an arithmetic type (double, int, ..) with a monomial
    template < typename T_Arithmetic,
               std::size_t t_id, std::size_t t_exponent0 >
    constexpr auto operator*(T_Arithmetic c, monomial < t_id, t_exponent0 > m0)
    -> monomial < t_id, t_exponent0 >
    {
        return monomial < t_id, t_exponent0 >{c * m0.coefficient};
    }
    // the other way 'round
    template < typename T_Arithmetic,
               std::size_t t_id, std::size_t t_exponent0 >
    constexpr auto operator*(monomial < t_id, t_exponent0 > m0, T_Arithmetic c)
    -> monomial < t_id, t_exponent0 >
    {
        return c * m0;
    }

    // multiply two monomials with the same id
    template < std::size_t t_id,
               std::size_t t_exponent0, std::size_t t_exponent1 >
    constexpr auto operator*(monomial < t_id, t_exponent0 > m0,
                             monomial < t_id, t_exponent1 > m1)
    -> monomial < t_id, t_exponent0 + t_exponent1 >
    {
        return monomial<t_id, t_exponent0 + t_exponent1>
                {m0.coefficient * m1.coefficient};
    }


    // storage type for multiple different monomials
    template < typename... T_Monomials >
    struct polynomial
    {
        void print() const
        {}
    };
        template < typename T_Monomial, typename... TT_Monomials >
        struct polynomial < T_Monomial, TT_Monomials... >
            : public polynomial < TT_Monomials... >
        {
            using base = polynomial < TT_Monomials... >;

            T_Monomial m;
            constexpr polynomial(T_Monomial p, TT_Monomials... pp)
                : base(pp...)
                , m{p}
            {}

            void print() const
            {
                m.print();
                std::cout << "*";
                base::print();
            }
        };

    // multiply two monomials to get a polynomial
    template < std::size_t t_id0, std::size_t t_id1,
               std::size_t t_exponent0, std::size_t t_exponent1 >
    constexpr auto operator*( monomial < t_id0, t_exponent0 > m0,
                              monomial < t_id1, t_exponent1 > m1)
    -> polynomial < monomial<t_id0, t_exponent0>,
                    monomial<t_id1, t_exponent1> >
    {
        return {m0, m1};
    }

    // still to do (and more complicated):
    // - multiply two polynomials
    // - multiply a polynomial and a monomial
    // - addition, subtraction, division (?) etc.
}

使用例:

int main()
{
    using namespace polynomials;

    auto p0 = 1.25*X*X;
    p0.print();
    std::cout << std::endl;

    auto p1 = p0 * 5*Y;
    p1.print();
    std::cout << std::endl;
}
于 2013-05-19T22:57:49.717 に答える