C ++ Matrix Class - PullRequest
       65

C ++ Matrix Class

23 голосов
/ 16 января 2010

В C, если бы я хотел создать матричную структуру, я бы использовал:

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

Тогда я могу выделить его с помощью

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 ++.

Ответы [ 11 ]

36 голосов
/ 16 января 2010

nota bene.

Этот ответ имеет 20 голосов "за", но он не предназначен для одобрения std::valarray.

По моему опыту, лучше потратить время на установку и обучение использованию полноценной математической библиотеки, такой как Eigen . У Valarray меньше возможностей, чем у конкурентов, но он не более эффективен и не особенно прост в использовании.

Если вам нужна только небольшая линейная алгебра, и вы абсолютно не можете добавить что-либо в свою цепочку инструментов, тогда, возможно, valarray подойдет. Но быть застрявшим в неспособности выразить математически правильное решение вашей проблемы - это очень плохое положение. Математика неумолима и не прощает. Используйте правильный инструмент для работы.


Стандартная библиотека предоставляет std::valarray<double>. std::vector<>, предложенный несколькими другими здесь, предназначен как контейнер общего назначения для объектов. valarray, менее известный, потому что он более специализированный (не использует «special» в качестве термина C ++), имеет несколько преимуществ:

  • Не выделяет дополнительного места. vector округляет до ближайшей степени два при распределении, так что вы можете изменить его размер без перераспределения каждый раз. (Вы все еще можете изменить размер valarray; это все равно так же дорого, как realloc().)
  • Вы можете нарезать его, чтобы легко получить доступ к строкам и столбцам.
  • Арифметические операторы работают так, как вы ожидаете.

Конечно, преимущество над C состоит в том, что вам не нужно управлять памятью. Размеры могут находиться в стеке или в объекте среза.

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
18 голосов
/ 16 января 2010

C ++ - это в основном надмножество C. Вы можете продолжать делать то, что делали.

Тем не менее, в C ++ вам нужно определить правильный класс Matrix, который управляет собственной памятью. Например, он может быть поддержан внутренним std::vector, и вы можете переопределить operator[] или operator(), чтобы соответствующим образом индексировать вектор (например, см .: Как создать оператор индекса для матрицы класс? ) из C ++ FAQ.

Для начала:

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.)

4 голосов
/ 16 января 2010

Существует множество тонкостей в создании эффективного и высококачественного класса матрицы. К счастью, есть несколько хороших реализаций.

Тщательно продумайте, хотите ли вы иметь матричный класс фиксированного размера или класс переменного размера. то есть вы можете сделать это:

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

или вам нужно это сделать

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

Есть хорошие библиотеки, которые поддерживают оба стиля, и некоторые, которые поддерживают оба. У них разные схемы распределения и разные характеристики.

Если вы хотите закодировать его самостоятельно, то версия шаблона требует некоторого знания шаблонов (дух). А для динамического нужны некоторые хаки, чтобы обойти множество небольших выделений при использовании внутри узких циклов.

3 голосов
/ 16 января 2010

Вы можете использовать шаблон как:

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

* редактирование, исправление опечатки.

3 голосов
/ 16 января 2010

Вы могли бы сделать это таким образом. Разница лишь в том, что вам нужно привести результат из malloc.

Скорее, вы бы использовали vector, либо как одномерный массив с вычисляемой индексацией, либо как встроенный вектор. (Первый лучше соответствует вашему коду.)

Например:

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

Или:

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

Но это только примеры. Вы хотели бы сделать полноценный класс; если вам нужны дополнительные советы по этому вопросу, отредактируйте свой вопрос и уточните, хотите ли вы знать канонический способ реализации матричных классов.

Существуют уже существующие классы матриц. Мой фаворит - это повышение, UBLAS .

2 голосов
/ 16 января 2010

Для класса матрицы вы хотите избежать перегрузки оператора [].
См. C ++ FAQ 13.10

Кроме того, поищите в Интернете некоторые бесплатные классы Matrix. В худшем случае, они могут дать вам руководство. В лучшем случае меньше программного обеспечения, которое вы должны написать и отладить .

2 голосов
/ 16 января 2010

вы можете сделать это с шаблоном, если размер матрицы известен во время компиляции:

template <int width, int height>
class Matrix{
    double data[height][width];
    //...member functions
};
1 голос
/ 16 июня 2018

Я написал матричную библиотеку , которая поддерживает так много функций.

Из документации:

Эта библиотека поддерживает математические операторы, такие как умножение, определитель, младший, кофактор и т. Д.

Usage

его использование аналогично массивам c ++.

Matrix<int> A(1, 2);
Matrix<int> B(2, 3);
Matrix<int> result(1, 3);

A[0][0] = 7;
A[0][1] = 10;

B[0][0] = 1;
B[0][1] = 4;
B[0][2] = 2;
B[1][0] = 1;
B[1][1] = 2;
B[1][2] = 100;

result = A * B;

result.dump.matrix();

Результат:

Matrix view:
-            -
| 17 48 1014 |
-            -

Вот документация

0 голосов
/ 27 сентября 2017

Не существует «канонического» способа сделать матрицу в C ++, STL не предоставляет классов, подобных «матрице».Однако есть некоторые сторонние библиотеки, которые делают.Вам предлагается использовать их или написать собственную реализацию.

0 голосов
/ 18 мая 2016

Github Link

//
//  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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...