C ++矩阵类

C++ Matrix Class

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

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

然后我可以分配给

1
2
3
4
5
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++中实现一个矩阵类的方法。


NoTa BeNe。

这个答案现在有20张赞成票,但并不打算作为std::valarray的支持。

根据我的经验,安装和学习使用一个成熟的数学库(如Eigen)的时间会更好。与竞争对手相比,Valarray的功能更少,但它的效率不高,使用起来也不特别容易。

如果您只需要一点线性代数,并且您已经完全准备好不向您的工具链中添加任何内容,那么valarray可能适合。但是,不能用数学正确的方法来表达你的问题是一个非常糟糕的位置。数学是无情和无情的。使用正确的工具进行作业。

标准库提供std::valarray。这里其他一些人提出的std::vector<>是一种用于存放物品的通用容器。EDCOX1,7,因为它更专业化(不使用专门的"C++术语"),有几个优点:

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

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

1
2
3
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


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

也就是说,在C++中,你应该做的是定义一个合适的矩阵类来管理它自己的内存。例如,它可以由一个内部的std::vector支持,您可以重写operator[]operator()以适当地索引到向量中(例如,请参见:如何为矩阵类创建下标运算符?)从C++常见问题解答。

要开始,请执行以下操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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
2
// These tend to be fast and allocated on the stack.
matrix<3,3> M;

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

1
2
// These are slower but more flexible and partially allocated on the heap
matrix M(3,3);

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

如果您想自己编写代码,那么模板版本需要一些模板知识(DUH)。如果在紧密的循环中使用,动态的需要一些黑客来绕过许多小的分配。


您可以使用如下模板:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#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;
        }
    }
}

*编辑,修正了打字错误。


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

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

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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] = /* ... */;

或:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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的。


对于矩阵类,您希望避免重载[]运算符。参见C++常见问题解答13.10

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


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

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


我已经编写了支持这么多特性的矩阵库。

从其文档中:

This library supports mathematics operators like multiplication, determinant, minor, cofactor and etc.

用法

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

1
2
3
4
5
6
7
8
Matrix<int> mat(2, 2);

mat[0][0] = 2;
mat[0][1] = 23;
mat[1][0] = 0;
mat[1][1] = -6;

cout << mat[1][0];

例如,要得到行列式:

1
cout << det(mat);

这是文件


C++中没有矩阵的"规范"方式,STL不提供像"矩阵"这样的类。但是也有一些第三方库可以做到。我们鼓励您使用它们或编写自己的实现。


吉图布链

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
//
//  iBS_Matrix.h
//
//
//  Created by nash on 11/29/15.
//  Copyright 2015 iBean Software.
//  All rights reserved.
//  current copy on Github:
//
#ifndef iBS_Matrix_h
#define iBS_Matrix_h

const int Matrix_MAJOR_VERSION = 1;
const int Matrix_MINOR_VERSION = 0;

#include <iostream>
#include <vector>
namespace iBS
{
struct Matrix
{
    std::vector<std::vector<int> > a;

    Matrix& operator =(Matrix& o)
    {
        a.resize(o.a.size());
        for(int i=0;i<a.size();i++)
            a[i].resize(o.a[i].size());
        for(int i=0;i<a.size();i++)
            for(int j=0;j<a[i].size();j++)
            {
                a[i][j] = o.a[i][j];
            }
        return *this;
    }

    Matrix& operator +(Matrix& o)
    {
        for(int i=0;i<a.size();i++)
            for(int j=0;j<a[i].size();j++)
            {
                a[i][j] = a[i][j] + o.a[i][j];
            }
        return *this;
    }
    Matrix& operator -(Matrix& o)
    {
        for(int i=0;i<a.size();i++)
            for(int j=0;j<a[i].size();j++)
            {
                a[i][j] = a[i][j] - o.a[i][j];
            }
        return *this;
    }
    Matrix& operator *(Matrix& o)
    {
        if(a[0].size() != o.a.size()) return *this;

        Matrix tm;
        tm.a.resize(a.size());
        for(int i=0;i<tm.a.size();i++)
            tm.a[i].resize(o.a[0].size());

        for(int i=0;i<tm.a.size();i++)
            for(int j=0;j<tm.a[i].size();j++)
            {
                tm.a[i][j] = 0;
                for (int c=0; c<a[i].size(); c++)
                {
                    tm.a[i][j] += a[i][c] * o.a[c][j];
                }

            }
        *this = tm;
        return *this;
    }
    Matrix& operator ^(int power)
    {
        Matrix  tM2;
        tM2 = *this;

    //   not <= below \/ because first time counts as 2
        for(int i=1; i<power; ++i)
            *this = (*this) * (tM2);

        return *this;
    }

    void print()
    {
        for(int i=0;i<a.size();i++)
        {
            for(int j=0;j<a[i].size();j++)
            {
                std::cout << a[i][j] << ' ';
            }
            std::cout << std::endl;
        }
        std::cout << std::endl;
    }
};

}; // end of namespace iBS

#endif // iBS_Matrix_h

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

1
matrix *p = new matrix;

之后,

1
delete p;