【问题标题】:C++ Matrix ClassC++ 矩阵类
【发布时间】:2011-01-05 19:19:50
【问题描述】:

在 C 中,如果我想创建一个矩阵结构,我会使用:

struct matrix {
  int col, row;
  double data[1]; // I want the matrix entries stored
                  // right after this struct
}

然后我可以分配它

matrix* allocate_matrix(int row, int col) {
  matrix* m = malloc(sizeof(matrix) + sizeof(double) * (row * col - 1));
  m->row = row; m->col = col;
  return m;
}

现在我要在 C++ 中做等价吗?

编辑:

我想知道在 C++ 中实现矩阵类的规范方法。

【问题讨论】:

    标签: c++ matrix


    【解决方案1】:

    注意。

    这个答案现在有 20 人赞成,但它并不是为了支持std::valarray

    根据我的经验,最好花时间安装和学习使用成熟的数学库,例如 Eigen。 Valarray 的功能比竞争对手少,但效率并不高,也不是特别容易使用。

    如果您只需要一点线性代数,并且坚决反对将任何东西添加到您的工具链中,那么valarray 可能适合。但是,陷入无法表达问题的数学正确解决方案的困境是一个非常糟糕的位置。数学是无情的和无情的。为工作使用正确的工具。


    标准库提供std::valarray<double>std::vector<>,这里的其他一些人建议,旨在作为对象的通用容器。 valarray,鲜为人知,因为它更专业(不使用“专业”作为 C++ 术语),有几个优点:

    • 它不分配额外的空间。 vector 在分配时四舍五入到最接近的 2 次方,因此您可以调整它的大小而无需每次都重新分配。 (您仍然可以调整 valarray 的大小;它仍然和 realloc() 一样昂贵。)
    • 您可以对其进行切片以轻松访问行和列。
    • 算术运算符按您的预期工作。

    当然,使用 C 的优势在于您不需要管理内存。维度可以驻留在堆栈中,也可以驻留在切片对象中。

    std::valarray<double> matrix( row * col ); // no more, no less, than a matrix
    matrix[ std::slice( 2, col, row ) ] = pi; // set third column to pi
    matrix[ std::slice( 3*row, row, 1 ) ] = e; // set fourth row to e
    

    【讨论】:

    • @Shep 不得不承认我从来没有真正使用过valarray……我尝试了几次,切片课程很痛苦。它可能值得一看,并且比这里的其他答案建议的那样滚动你自己的更好,但是那里有更好的库。 (好吧,至少看起来是这样,我还没有真正使用过。不过,Boost BLAS 应该不错。)
    • 是的,这是走向 10 票。 std::valarray 比较难用,只支持基本的逐成员代数,不是很流行。试试Eigen Library,它非常易于使用、功能强大且很受欢迎。
    • Armadillo C++ linear algebra library 也值得一看:它的syntax/API 与 Matlab 非常相似。
    【解决方案2】:

    C++ 主要是 C 的超集。你可以继续做你正在做的事情。

    也就是说,在 C++ 中,你应该做的是定义一个适当的 Matrix 类来管理它自己的内存。例如,它可以由内部 std::vector 支持,您可以覆盖 operator[]operator() 以适当地索引到向量中(例如,请参阅 C++ 常见问题解答中的 How do I create a subscript operator for a Matrix class?)。

    让您开始:

    class Matrix
    {
    public:
        Matrix(size_t rows, size_t cols);
        double& operator()(size_t i, size_t j);
        double operator()(size_t i, size_t j) const;
    
    private:
        size_t mRows;
        size_t mCols;
        std::vector<double> mData;
    };
    
    Matrix::Matrix(size_t rows, size_t cols)
    : mRows(rows),
      mCols(cols),
      mData(rows * cols)
    {
    }
    
    double& Matrix::operator()(size_t i, size_t j)
    {
        return mData[i * mCols + j];
    }
    
    double Matrix::operator()(size_t i, size_t j) const
    {
        return mData[i * mCols + j];
    }
    

    (请注意,上面没有做任何边界检查,我把它作为一个练习来模板化它,以便它适用于double以外的东西。)

    【讨论】:

    • 我最喜欢这个答案,它保持美观和简单,而且向量非常好。
    • +1 来自我。我只想提一下,这是boost:multi_array 所做的基本思想(它可以推广到任意数量的维度)。 boost 与矩阵的stl 一样接近,如果您愿意,它们也有专门用于 2 或 3 维的矩阵。最后,要记住的一件事是 matrix&lt;bool&gt; 容器将继承 std::vector&lt;bool&gt; 的问题,除非得到解决。
    【解决方案3】:

    可以那样做。唯一的区别是您需要从malloc 转换结果。

    相反,您可以使用vector,作为具有计算索引的一维数组或嵌入向量。 (前者更符合您的代码。)

    例如:

    template <typename T> // often, they are templates
    struct matrix
    {
        // should probably be hidden away, and the class would
        // provide `at` and `operator()` for access
        int col, row;
        std::vector<T> data;
    
        matrix(int columns, int rows) :
        col(columns), row(rows), 
        data(col * row)
        {}
    
    }
    
    matrix m(4, 4);
    m.data[1 + 1 * 4] = /* ... */;
    

    或者:

    template <typename T>
    struct matrix
    {
        int col, row;
        std::vector<std::vector<T> > data;
    
        matrix(int columns, int rows) :
        col(columns), row(rows), 
        data(col, std::vector(row))
        {}
    }
    
    matrix m(4, 4);
    m.data[1][1] = /* ... */;
    

    但这些只是示例。你想上一堂成熟的课;如果您想获得更多建议,请编辑您的问题并说明您想了解实现矩阵类的规范方法。

    有预先存在的矩阵类。我最喜欢的是来自 boost,UBLAS

    【讨论】:

    • 你的意思是data[1][1],而不是data[1, 1]。 (从技术上讲,m.data[1][1]。)
    • 哎呀,完全搞砸了。
    • 我认为vector 中的vector 不会是一个非常有效的实现。
    • @bobobobo:这就是为什么它是一个替代答案。不过,可能无论如何都不会注意到任何区别。
    【解决方案4】:

    建立一个高效和高质量的矩阵类有很多微妙之处。值得庆幸的是,有几个很好的实现。

    认真考虑您想要一个固定大小的矩阵类还是一个可变大小的矩阵类。 即你能这样做吗:

    // These tend to be fast and allocated on the stack.
    matrix<3,3> M; 
    

    或者你需要能够做到这一点

    // These tend to be slower but more flexible and partially allocated on the heap 
    matrix M(3,3); 
    

    有一些很好的库支持这两种风格,还有一些同时支持这两种风格。 它们有不同的分配模式和不同的性能。

    如果您想自己编写代码,那么模板版本需要一些模板知识(呵呵)。如果在紧密循环中使用动态分配,则需要一些技巧来解决大量小分配问题。

    【讨论】:

    • 第一个选项实际上可以 slower 因为它的数组不是浅拷贝的(除非实现者选择使用编译时信息来分配给堆,这是不可能的)。 “堆栈比堆快”的概念是不正确的 - 这取决于。
    • 将分配放在堆栈或堆上是一个不同的设计决策。固定尺寸和可变尺寸都可以放在那里。哪个合适将在很大程度上取决于确切的用例。浅拷贝是另一个设计决策——它带来了一系列全新的问题——主要与底层缓冲区的所有权和生命周期有关。 (我个人避免对矩阵进行浅拷贝,并避免查看到矩阵,除非性能分析表明这是一个问题)。
    • 我想我们同意这里。我刚刚提到它是因为这个答案指出“这些 [堆分配的矩阵] 速度较慢”,我认为我们都同意这有点误导。我知道这个答案是十年前给出的,但这是一个很好的答案,我认为您可能想要更新。 =)
    【解决方案5】:

    你可以使用这样的模板:

    #include <iostream>
    using std::cerr;
    using std::endl;
    
    //qt4type
    typedef unsigned int quint32;
    
    template <typename T>
    void deletep(T &) {}
    template <typename T>
    void deletep(T* & ptr) {
        delete ptr;
        ptr = 0;
    }
    template<typename T>
    class Matrix {
        public:
            typedef T value_type;
            Matrix() : _cols(0), _rows(0), _data(new T[0]), auto_delete(true) {};
            Matrix(quint32 rows, quint32 cols, bool auto_del = true);
    
            bool exists(quint32 row, quint32 col) const;
            T & operator()(quint32 row, quint32 col);
            T operator()(quint32 row, quint32 col) const;
            virtual ~Matrix();
    
            int size() const { return _rows * _cols; }
            int rows() const { return _rows; }
            int cols() const { return _cols; }
        private:
            Matrix(const Matrix &);
            quint32 _rows, _cols;
            mutable T * _data;
            const bool auto_delete;
    };
    template<typename T>
    Matrix<T>::Matrix(quint32 rows, quint32 cols, bool auto_del) : _rows(rows), _cols(cols), auto_delete(auto_del) {
        _data = new T[rows * cols];
    }
    template<typename T>
    inline T & Matrix<T>::operator()(quint32 row, quint32 col) {
        return _data[_cols * row + col];
    }
    template<typename T>
    inline T Matrix<T>::operator()(quint32 row, quint32 col) const {
        return _data[_cols * row + col];
    }
    
    template<typename T>
    bool Matrix<T>::exists(quint32 row, quint32 col) const {
        return (row < _rows && col < _cols);
    }
    
    template<typename T>
    Matrix<T>::~Matrix() {
        if(auto_delete){
            for(int i = 0, c = size(); i < c; ++i){
                //will do nothing if T isn't a pointer
                deletep(_data[i]);
            }
        }
        delete [] _data;
    }
    
    int main() {
        Matrix< int > m(10,10);
        quint32 i = 0;
        for(int x = 0; x < 10; ++x) {
            for(int y = 0; y < 10; ++y, ++i) {
                m(x, y) = i;
            }
        }
        for(int x = 0; x < 10; ++x) {
            for(int y = 0; y < 10; ++y) {
                cerr << "@(" << x << ", " << y << ") : " << m(x,y) << endl;
            }
        }
    }
    

    *编辑,修正了一个错字。

    【讨论】:

    • 为什么析构函数需要是虚拟的?
    【解决方案6】:

    如果矩阵大小在编译时已知,您可以使用模板来完成:

    template <int width, int height>
    class Matrix{
        double data[height][width];
        //...member functions
    };
    

    【讨论】:

    • 为了完成,你可以用Matrix&lt;3, 3&gt; matrix;实例化这个新的矩阵
    【解决方案7】:

    对于矩阵类,您希望避免重载 [] 运算符。
    C++ FAQ 13.10

    另外,在网上搜索一些免费软件 Matrix 类。最坏的情况,他们可以给你指导。最好的情况,您必须编写和调试的软件更少。

    【讨论】:

      【解决方案8】:

      在 C++ 中没有“规范”的方法来处理矩阵,STL 不提供像“矩阵”这样的类。但是有一些 3rd 方库可以。我们鼓励您使用它们或编写自己的实现。你可以试试my implementation 源自互联网上的一些公共实现。

      【讨论】:

        【解决方案9】:

        名为Matrix 的库支持许多功能,包括数学运算、转储和日志记录功能、关联容器、多维等。

        用法

        它的用法类似于 c++ 数组。

        Matrix<int> A(1, 2);
        Matrix<int> B(2, 3);
        Matrix<int> result(1, 3);
        
        A[0][0] = 7;
        A[0][1] = 10;
        
        B[0][0] = 1;
        B[0][1] = 4;
        B[0][2] = 2;
        B[1][0] = 1;
        B[1][1] = 2;
        B[1][2] = 100;
        
        result = A * B;
        
        result.dump.matrix();
        

        结果:

        Matrix view:
        -            -
        | 17 48 1014 |
        -            -
        

        这里是documentationGithub page

        【讨论】:

        • 使用命名空间标准;在图书馆?请不要这样做!
        【解决方案10】:

        在 C++ 中你可以这样使用:

        matrix *p = new matrix;
        

        之后,

        delete p; 
        

        【讨论】:

        • 问题的原始措辞中没有明确说明 OP,但其意图显然是 struct 的最后一个成员是可变/不确定大小的数组,因此只需使用 @987654324 @ 而不是 malloc 将不起作用。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多