web-dev-qa-db-fra.com

Classe matricielle C++

En C, si je voulais créer une structure de matrice, j'utiliserais:

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

Ensuite, je peux le répartir avec

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

Maintenant, est-ce que je fais l'équivalent en C++?

MODIFIER:

Je souhaite connaître la méthode classique pour implémenter une classe de matrice en C++.

19
anon

nota bene.

Cette réponse a maintenant 20 votes positifs, mais il ne s'agit pas d'un endossement de std::valarray.

D'après mon expérience, il vaut mieux passer du temps à installer et apprendre à utiliser une bibliothèque mathématique complète telle que Eigen . Valarray a moins de fonctionnalités que la concurrence, mais il n'est ni plus efficace ni particulièrement facile à utiliser.

Si vous n'avez besoin que d'un peu d'algèbre linéaire, et que vous êtes fermement décidé à ne rien ajouter à votre chaîne d'outils, alors peut-être que valarray conviendrait. Mais être bloqué dans l'impossibilité d'exprimer la solution mathématiquement correcte à votre problème est une très mauvaise position. Les mathématiques sont implacables et impitoyables. Utilisez le bon outil pour le travail.


La bibliothèque standard fournit std::valarray<double> . std::vector<>, suggéré par quelques autres ici, est conçu comme un conteneur à usage général pour les objets. valarray, moins connu parce qu'il est plus spécialisé (n'utilisant pas le terme "spécialisé" en tant que terme C++), présente plusieurs avantages:

  • Il n'alloue pas d'espace supplémentaire. vector arrondit à la puissance deux la plus proche lors de l'allocation, ce qui vous permet de le redimensionner sans réallouer à chaque fois. (Vous pouvez toujours redimensionner une valarray; c'est toujours aussi coûteux que realloc().)
  • Vous pouvez le découper pour accéder facilement aux lignes et aux colonnes.
  • Les opérateurs arithmétiques fonctionnent comme prévu.

Bien entendu, l’avantage de l’utilisation de C est que vous n’avez pas besoin de gérer la mémoire. Les dimensions peuvent résider sur la pile ou dans un objet tranche.

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
33
Potatoswatter

C++ est principalement un sur-ensemble de C. Vous pouvez continuer à faire ce que vous faisiez.

Cela dit, en C++, vous devez définir une classe Matrix appropriée qui gère sa propre mémoire. Il pourrait, par exemple, être soutenu par un std::vector interne et vous pouvez remplacer le operator[] ou le operator() pour l'indexer correctement dans le vecteur (par exemple, voir: Comment créer un opérateur en indice pour une classe Matrix? ) à partir du FAQ C++.

Pour vous aider à démarrer:

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

(Notez que ce qui précède ne fait aucune vérification des limites, et je le laisse comme exercice pour le modéliser afin qu'il fonctionne pour des choses autres que double.)

16
jamesdlin

Il y a beaucoup de subtilités dans la mise en place d'une classe matricielle efficace et de haute qualité. Heureusement, plusieurs bonnes implémentations flottent.

Réfléchissez bien pour savoir si vous voulez une classe de matrice de taille fixe ou une classe de taille variable. pouvez-vous faire cela:

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

ou avez-vous besoin de pouvoir le faire

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

Il existe de bonnes bibliothèques qui prennent en charge l'un ou l'autre style, et certaines qui le font… .. Elles ont des modèles d'allocation et des performances différentes. 

Si vous souhaitez le coder vous-même, la version du modèle nécessite une connaissance des modèles (duh). Et la dynamique a besoin de quelques astuces pour éviter beaucoup de petites allocations si elle est utilisée dans des boucles serrées.

4
Michael Anderson

Vous pouvez utiliser un modèle comme:

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

* modifier, corrigé une faute de frappe.

3
OneOfOne

Vous pourriez le faire de cette façon. La seule différence est que vous devez convertir le résultat de malloc.

Vous utiliseriez plutôt une vector, sous forme de tableau 1D avec indexation calculée ou de vecteur incorporé. (Le premier correspond mieux à votre code.)

Par exemple:

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] = /* ... */;

Ou:

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] = /* ... */;

Mais ce ne sont que des exemples. Vous voudriez faire une classe à part entière; Si vous souhaitez davantage de conseils à ce sujet, éditez votre question et expliquez que vous souhaitez connaître la manière canonique d'implémenter des classes de matrice.

Il existe des classes de matrice préexistantes. Mon préféré est celui de boost, UBLAS .

3
GManNickG

Pour une classe de matrice, vous voulez éviter de surcharger l'opérateur [].
Voir C++ FAQ 13.10

Recherchez également sur le Web des classes Matrix gratuites. Dans le pire des cas, ils peuvent vous donner des conseils. Le meilleur des cas, moins de logiciels que vous devez écrire et debug.

2
Thomas Matthews

vous pouvez le faire avec un template si la taille de la matrice est connue au moment de la compilation:

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

Lien Github

//
//  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
0
Nash Bean

J'ai écrit bibliothèque Matrix qui prend en charge de nombreuses fonctionnalités.

De sa documentation:

Cette bibliothèque prend en charge des opérateurs mathématiques tels que multiplication, déterminant, mineur, cofacteur, etc. 

Utilisation

son utilisation est similaire à celle des tableaux c ++.

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

Pour avoir un déterminant par exemple:

cout << det(mat);

Voici la documentation

0
Amir Forsati

Il n'y a pas de manière "canonique" de faire la matrice en C++, STL ne fournit pas de classes comme "matrix". Cependant, certaines bibliothèques tierces le font. Nous vous encourageons à les utiliser ou à écrire votre propre implémentation.

0
ivan.ukr