web-dev-qa-db-fra.com

Une bonne façon de créer une matrice en c ++

Je veux créer une matrice d'adjacence pour un graphique. Depuis que j'ai lu, il n'est pas sûr d'utiliser des tableaux de la forme matrix[x][y] parce qu'ils ne vérifient pas la plage, j'ai décidé d'utiliser la classe de modèles vectoriels de la stl. Tout ce que j'ai besoin de stocker dans la matrice, ce sont des valeurs booléennes. Donc ma question est, si vous utilisez std::vector<std::vector<bool>* >* produit trop de surcharge ou s'il existe un moyen plus simple pour une matrice et comment je peux l'initialiser correctement.

EDIT: Merci beaucoup pour les réponses rapides. Je viens de réaliser que bien sûr je n'ai pas besoin de pointeurs. La taille de la matrice sera initialisée dès le début et ne changera pas avant la fin du programme. C'est pour un projet d'école, donc ce serait bien si j'écris du code "Nice", bien que techniquement les performances ne soient pas trop importantes. L'utilisation de la STL est très bien. Utiliser quelque chose comme boost, n'est probablement pas apprécié.

27
Lucas

Notez que vous pouvez également utiliser boost.ublas pour la création et la manipulation de matrice et également boost.graph pour représenter et manipuler les graphiques de plusieurs façons, ainsi qu'en utilisant des algorithmes sur eux, etc.

Edit : Quoi qu'il en soit, faire une version de vérification de portée d'un vecteur à vos fins n'est pas une chose difficile:

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

Notez que vous devrez également ajouter la version const des opérateurs et/ou des itérateurs, et l'utilisation étrange des exceptions, mais vous avez l'idée.

18
Diego Sevilla

Le vecteur standard ne fait PAS de vérification de plage par défaut.

c'est-à-dire que l'opérateur [] ne fait pas de vérification de portée.

La méthode à () est similaire à [] mais fait une vérification de plage.
Il lèvera une exception hors de portée.

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

Autres remarques: Pourquoi un vecteur <Pointeurs>?
Vous pouvez très facilement avoir un vecteur <Objet>. Désormais, vous n'avez plus à vous soucier de la gestion de la mémoire (c'est-à-dire des fuites).

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

Remarque: le vecteur <bool> est surchargé et peu efficace (c'est-à-dire que cette structure a été optimisée pour la taille et non la vitesse) (c'est quelque chose qui est maintenant reconnu comme probablement une erreur par le comité des normes).

Si vous connaissez la taille de la matrice au moment de la compilation, vous pouvez utiliser std :: bitset?

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

ou s'il est défini à l'exécution, utilisez boost :: dynamic_bitset

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

Tout ce qui précède vous permettra de faire:

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

Meilleur moyen:

Créez votre propre classe de matrice, de cette façon vous en contrôlez tous les aspects, y compris la vérification de la plage.

par exemple. Si vous aimez la notation "[x] [y]", faites ceci:

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. Si vous programmez ainsi, C++ est tout aussi sûr que tous les autres langages "sûrs".

8
Jimmy J

Si vous souhaitez des performances de tableau "C", mais avec une sécurité supplémentaire et une sémantique de type STL (itérateurs, begin() & end() etc.), utilisez boost::array .

Fondamentalement, il s'agit d'un wrapper basé sur des modèles pour les tableaux en "C" avec quelques assertions de vérification de plage désactivables NDEBUG- (et également quelques std::range_error accesseurs leveurs d'exceptions).

J'utilise des trucs comme

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

au lieu de

float m[4][4];

tout le temps et cela fonctionne très bien (avec les typedefs appropriés pour garder la verbosité, de toute façon).


MISE À JOUR: Suite à une discussion dans les commentaires ici de la performance relative de boost::array contre boost::multi_array, Je précise que ce code, compilé avec g++ -O3 -DNDEBUG sur Debian/Lenny AMD64 sur un Q9450 avec 1333 MHz DDR3 RAM prend 3,3 secondes pour boost::multi_array vs 0,6 s pour boost::array.

#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

Ce que je ferais, c'est de créer ma propre classe pour traiter les matrices (probablement sous forme de tableau [x * y] parce que je suis plus habitué au C (et j'aurais mes propres vérifications de limites), mais vous pouvez utiliser des vecteurs ou tout autre autre sous-structure de cette classe).

Faites fonctionner vos trucs d'abord, puis préoccupez-vous de la vitesse à laquelle ils fonctionnent. Si vous concevez la classe correctement, vous pouvez retirer votre implémentation de tableau [x * y] et la remplacer par des vecteurs ou des masques de bits ou tout ce que vous voulez sans changer le reste du code.

Je ne suis pas totalement sûr, mais je pense que c'est à cela que les classes étaient destinées, la possibilité d'abstraire l'implémentation à l'abri des regards et de ne fournir que l'interface :-)

3
paxdiablo

ma façon préférée de stocker un graphique est vector<set<int>>; n éléments dans le vecteur (nœuds 0..n-1),> = 0 éléments dans chaque ensemble (arêtes). N'oubliez pas d'ajouter une copie inversée de chaque bord bidirectionnel.

3
OverInflatedWalrus

En plus de toutes les réponses qui ont été publiées jusqu'à présent, vous pourriez faire bien de vérifier le C++ FAQ Lite . Questions 13.10 - 13.12 et 16.16 - 16.19 couvrent plusieurs sujets liés au roulement de votre propre classe de matrice. Vous verrez deux façons différentes de stocker les données et des suggestions sur la meilleure façon d'écrire les opérateurs d'indice.

De plus, si votre graphique est suffisamment clairsemé, vous n'avez peut-être pas du tout besoin d'une matrice. Vous pouvez utiliser std::multimap pour mapper chaque sommet à ceux qu'il relie.

3

Considérez également la taille de votre graphique/matrice, les performances sont-elles importantes? Le graphique est-il statique ou peut-il évoluer avec le temps, par exemple en ajoutant de nouveaux bords?

1
siddhadev

Remarquez std::vector ne vérifie pas la plage non plus.

1
shoosh

Probablement pas pertinent car c'est une vieille question, mais vous pouvez utiliser la bibliothèque Armadillo , qui fournit de nombreux types de données et fonctions orientées algèbre linéaire.

Voici un exemple de votre problème spécifique:

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

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