web-dev-qa-db-ja.com

C ++で行列を作成する適切な方法

グラフの隣接行列を作成します。範囲をチェックしないため、matrix[x][y]形式の配列を使用するのは安全ではないことを読んだので、stlのベクターテンプレートクラスを使用することにしました。マトリックスに保存する必要があるのはブール値だけです。したがって、私の質問は、std::vector<std::vector<bool>* >*を使用するとオーバーヘッドが大きくなりすぎる場合、またはマトリックスにもっと簡単な方法があり、適切に初期化できるかどうかです。

編集:迅速な回答をありがとうございました。もちろん、ポインタは必要ありません。行列のサイズは最初に初期化され、プログラムの最後まで変更されません。これは学校のプロジェクト用なので、技術的にパフォーマンスはそれほど重要ではありませんが、「ナイス」コードを書くとよいでしょう。 STLの使用は問題ありません。ブーストのようなものを使用することはおそらくお勧めできません。

27
Lucas

boost.ublas を使用してマトリックスを作成および操作したり、 boost.graph を使用してグラフをさまざまな方法で表現および操作したり、それらなど.

編集:とにかく、あなたの目的のためにベクトルの範囲チェックバージョンを行うことは難しいことではありません:

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];
        }
};

演算子や反復子のconstバージョン、および例外の奇妙な使用も​​追加する必要があることに注意してください。

18
Diego Sevilla

標準ベクトルは、デフォルトでは範囲チェックを行いません。

つまり、operator []は範囲チェックを行いません。

At()メソッドは[]に似ていますが、範囲チェックを行います。
範囲外で例外がスローされます。

std :: vector :: at()
std :: vector :: operator []()

その他の注意:なぜvector <Pointers>なのか?
vector <Object>を簡単に作成できます。これで、メモリ管理(つまり、リーク)を心配する必要がなくなりました。

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

注:vector <bool>はオーバーロードされており、あまり効率的ではありません(つまり、この構造は速度ではなくサイズに合わせて最適化されています)(これは、おそらく標準委員会によって間違いとして認識されているものです)。

コンパイル時に行列のサイズがわかっている場合は、std :: bitset?

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

または、ランタイムで定義されている場合は、boost :: dynamic_bitsetを使用します。

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

上記のすべてができるようになります:

m[6][3] = true;
10
Martin York

最良の方法:

独自の行列クラスを作成します。これにより、範囲チェックを含む、その最後のすべての側面を制御できます。

例えば。 「[x] [y]」表記が気に入ったら、次のようにします。

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;

nb。このようにプログラムすると、C++は他のすべての「安全な」言語と同じくらい安全です。

8
Jimmy J

'C'配列のパフォーマンスが必要だが、安全性とSTLに似たセマンティクス(イテレータ、begin()end()など)が必要な場合は、 boost::array

基本的には、いくつかのNDEBUG- disable-able範囲チェックアサート(およびいくつかのstd::range_error例外をスローするアクセサー)を備えた 'C'配列のテンプレートラッパーです。

私は次のものを使用します

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

の代わりに

float m[4][4];

常に機能します(とにかく冗長性を低く抑えるための適切なtypedefを使用します)。


UPDATE:boost::arrayboost::multi_arrayの相対的なパフォーマンスについてのコメントでの議論に続いて、私はそれを指摘しますこのコードは、1333MHz DDR3を搭載したQ9450のDebian/Lenny AMD64でg++ -O3 -DNDEBUGを使用してコンパイルされていますRAM boost::multi_arrayには3.3秒、boost::arrayには0.6秒かかります。

#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<array<array<char,N>,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\n"
    << "array      : " << (t2-t1)/static_cast<float>(CLOCKS_PER_SEC) << "s\n";

  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;
}
5
timday

私がやろうとしていることは、行列を処理するための独自のクラスを作成することです(おそらく配列[x * y]としてCに慣れているため(そして私は独自の境界チェックを持っているため))、ベクトルや任意のそのクラスの他のサブ構造)。

最初に機能を取得してから、実行速度を心配します。クラスを適切に設計すると、array [x * y]実装を引き出して、残りのコードを変更せずに、ベクターやビットマスクなど、必要なものに置き換えることができます。

私は完全にはわかりませんが、それはクラスが意図されていたものであり、見えないところに実装を抽象化し、インターフェイスのみを提供する機能です:-)

3
paxdiablo

グラフを保存する私のお気に入りの方法はvector<set<int>>;ベクトルのn要素(ノード0..n-1)、各セット(エッジ)の> = 0要素。すべての双方向Edgeの逆のコピーを追加することを忘れないでください。

3

これまでに投稿されたすべての回答に加えて、 C++ FAQ Lite 質問 13.10-13.12 および 16.16-16.19 は、関連するいくつかのトピックをカバーしています独自のマトリックスクラスをローリングする方法を説明します。データを格納するいくつかの異なる方法と、添字演算子の最適な記述方法に関する提案が表示されます。

また、グラフが十分にスパースである場合、マトリックスはまったく必要ない場合があります。 std::multimap各頂点を、接続する頂点にマッピングします。

3

グラフ/行列の大きさも考慮してください。パフォーマンスは重要ですか?グラフは静的ですか、それとも時間の経過とともに拡大する可能性がありますか。新しいエッジを追加することによって?

1
siddhadev

心に留めてstd::vectorも範囲チェックを行いません。

1
shoosh

おそらく、これは古い質問なので関係ありませんが、 Armadillo ライブラリを使用できます。これは、多くの線形代数指向のデータ型と関数を提供します。

以下は、特定の問題の例です。

// In C++11
Mat<bool> matrix = {  
    { true, true},
    { false, false},
};

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