关于数据结构:在c ++中创建矩阵的正确方法

A proper way to create a matrix in c++

我想为一个图创建一个邻接矩阵。因为我读到使用matrix[x][y]形式的数组是不安全的,因为它们不检查范围,所以我决定使用stl的向量模板类。我只需要在矩阵中存储布尔值。所以我的问题是,如果使用std::vector* >*会产生太多的开销,或者对于一个矩阵有一个更简单的方法,以及如何正确地初始化它。

编辑:非常感谢你的快速回答。我刚刚意识到,我当然不需要任何指针。矩阵的大小将在开始时初始化,直到程序结束时才会更改。这是一个学校项目,所以如果我写"不错"的代码就好了,尽管技术性能并不太重要。使用STL很好。使用像boost之类的东西,可能是不受欢迎的。


请注意,您还可以使用boost.ublas创建和操作矩阵,也可以使用boost.graph以多种方式表示和操作图形,以及对它们使用算法等。

编辑:不管怎样,为您的目的执行向量的范围检查版本并不难:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
template <typename T>
class BoundsMatrix
{
        std::vector<T> inner_;
        unsigned int dimx_, dimy_;

public:
        BoundsMatrix (unsigned int dimx, unsigned int dimy)
                : dimx_ (dimx), dimy_ (dimy)
        {
                inner_.resize (dimx_*dimy_);
        }

        T& operator()(unsigned int x, unsigned int y)
        {
                if (x >= dimx_ || y>= dimy_)
                        throw std::out_of_range("matrix indices out of range"); // ouch
                return inner_[dimx_*y + x];
        }
};

请注意,您还需要添加运算符和/或迭代器的常量版本,以及异常的奇怪用法,但是您得到了这个想法。


默认情况下,标准向量不进行范围检查。

即,操作员[]不进行范围检查。

()处的方法与[]类似,但执行范围检查。它将引发超出范围的异常。

std::vector::at()。标准::矢量::运算符[]()

其他注释:为什么是矢量?你可以很容易地得到一个向量。现在不需要担心内存管理(即泄漏)。

1
std::vector<std::vector<bool> >   m;

注:vector过载且效率不高(即,此结构针对大小而非速度进行了优化)(现在标准委员会认为这可能是一个错误)。

如果在编译时知道矩阵的大小,可以使用std::bitset?

1
std::vector<std::bitset<5> >    m;

或者,如果它是运行时定义的,则使用boost::dynamic位集

1
std::vector<boost::dynamic_bitset>  m;

以上所有内容都允许您执行以下操作:

1
m[6][3] = true;


最佳方式:

创建自己的矩阵类,这样就可以控制它的每一个方面,包括范围检查。

如果你喜欢用"[X][Y]"表示法,可以这样做:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class my_matrix {
  std::vector<std::vector<bool> >m;
public:
  my_matrix(unsigned int x, unsigned int y) {
    m.resize(x, std::vector<bool>(y,false));
  }
  class matrix_row {
    std::vector<bool>& row;
  public:
    matrix_row(std::vector<bool>& r) : row(r) {
    }
    bool& operator[](unsigned int y) {
      return row.at(y);
    }
  };
  matrix_row& operator[](unsigned int x) {
    return matrix_row(m.at(x));
  }
};
// Example usage
my_matrix mm(100,100);
mm[10][10] = true;

铌。如果你这样编程,那么C++和所有其他"安全"语言一样安全。


如果您想要"c"数组性能,但添加了安全性和类STL的语义(迭代器、begin()end()等),请使用boost::array

基本上,它是一个"c"数组的模板包装器,带有一些NDEBUG禁用的范围检查断言(以及一些std::range_error异常抛出访问器)。

我用的东西像

1
boost::array<boost::array<float,4>,4> m;

而不是

1
float m[4][4];

一直以来,它都很好地工作(不管怎样,使用适当的typedef来保持冗长)。

更新:在这里讨论了boost::arrayboost::multi_array的相对性能之后,我指出,用g++ -O3 -DNDEBUG在带有1333MHz DDR3 RAM的Q9450上对debian/lenny amd64编译的代码,boost::multi_array需要3.3s,boost::array需要0.6s。

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
#include <iostream>
#include <time.h>
#include"boost/array.hpp"
#include"boost/multi_array.hpp"

using namespace boost;

enum {N=1024};

typedef multi_array<char,3> M;
typedef array,N>,N> C;

// Forward declare to avoid being optimised away
static void clear(M& m);
static void clear(C& c);

int main(int,char**)
{
  const clock_t t0=clock();

  {
    M m(extents[N][N][N]);
    clear(m);
  }

  const clock_t t1=clock();

  {
    std::auto_ptr<C> c(new C);
    clear(*c);
  }

  const clock_t t2=clock();

  std::cout
    <<"multi_array:" << (t1-t0)/static_cast<float>(CLOCKS_PER_SEC) <<"s
"

    <<"array      :" << (t2-t1)/static_cast<float>(CLOCKS_PER_SEC) <<"s
"
;

  return 0;
}

void clear(M& m)
{
  for (M::index i=0;i<N;i++)
    for (M::index j=0;j<N;j++)
      for (M::index k=0;k<N;k++)
    m[i][j][k]=1;
}


void clear(C& c)
{
  for (int i=0;i<N;i++)
    for (int j=0;j<N;j++)
      for (int k=0;k<N;k++)
    c[i][j][k]=1;
}


我最喜欢的图形存储方法是vector>;n个元素在向量中(节点0..n-1),每个集合(边)中的元素数大于等于0。只是不要忘记添加每个双向边缘的反向副本。


除了迄今发布的所有答案之外,您还可以检查C++ FAQ Lite。问题13.10-13.12和16.16-16.19包括与滚动自己的矩阵类相关的几个主题。您将看到存储数据的几种不同方法,以及如何最好地编写下标运算符的建议。

此外,如果图足够稀疏,则可能根本不需要矩阵。您可以使用std::multimap将每个顶点映射到它所连接的顶点。


我要做的是创建自己的类来处理矩阵(可能作为一个数组[x*y],因为我更习惯于C(并且我有自己的边界检查),但是你可以在该类中使用向量或任何其他子结构。

先让你的东西发挥功能,然后再担心它跑得有多快。如果您正确地设计了类,那么您可以拉出数组[x*y]实现,并用向量或位掩码或您想要的任何东西替换它,而不必更改其余的代码。

我不完全确定,但我认为这就是类的意义所在,它能够很好地抽象实现,并且只提供接口:—)


可能与此无关,因为这是一个古老的问题,但是您可以使用Armadillo库,它提供许多面向线性代数的数据类型和函数。

下面是您具体问题的示例:

1
2
3
4
5
6
7
8
9
10
// In C++11
Mat<bool> matrix = {  
    { true, true},
    { false, false},
};

// In C++98
Mat<bool> matrix;
matrix << true << true << endr
       << false << false << endr;

请注意,std::vector也不进行范围检查。


还要考虑你的图表/矩阵有多大,性能有多重要?图形是静态的,还是可以随着时间的推移而增长,例如通过添加新的边?