Предположим, я представляю матрицу foo
значений с использованием std::vector
:
int rows = 5;
int cols = 10;
auto foo = vector<vector<double>>(rows, vector<double>(cols));
Есть ли для меня умно простой способ получить vector<int>
размера rows
который содержит первый «столбец» foo:
{foo[0][0], foo[0][1], foo[0][2], foo[0][3], foo[0][4] }
Другими словами, могу ли я «транспонировать» foo так, чтобы выполнялись следующие три вещи:
foo_transpose.size() == cols
foo_transpose[0].size() == rows
foo_transpose[0] == {foo[0][0], foo[0][1], foo[0][2], foo[0][3], foo[0][4] }
Есть несколько хороших предложений по альтернативным способам представления «матрицы». Когда я использую термин «матрица», я просто имею в виду, что каждый второй уровень vector
будет такого же размера. Я не имею в виду, что я буду использовать эту структуру данных для операций типа линейной алгебры. Мне действительно нужен вектор векторов или структура данных, из которой вы можете «извлекать» одномерные векторы, потому что у меня есть функции, которые работают с такими векторами, как:
double sum(vector<double> const & v);
Это я называю по:
sum(foo[0]);
Это просто в особом случае я столкнулся с ситуацией, которую нужно сделать:
sum({foo[0][0], foo[0][1], foo[0][2], foo[0][3], foo[0][4] };
Существует очевидное решение для петель, но я искал что-то более надежное и эффективное.
Как я упоминал в комментариях, нецелесообразно представлять матрицы с использованием вектора-вектора по нескольким причинам:
Вот очень простой класс, который я создал, который будет содержать 2D-матрицу в одном векторе. Это в значительной степени то, как это делает программное обеспечение, такое как MATLAB … хотя и огромное упрощение.
template <class T>
class SimpleMatrix
{
public:
SimpleMatrix( int rows, int cols, const T& initVal = T() );
// Size and structure
int NumRows() const { return m_rows; }
int NumColumns() const { return m_cols; }
int NumElements() const { return m_data.size(); }
// Direct vector access and indexing
operator const vector<T>& () const { return m_data; }
int Index( int row, int col ) const { return row * m_cols + col; }
// Get a single value
T & Value( int row, int col ) { return m_data[Index(row,col)]; }
const T & Value( int row, int col ) const { return m_data[Index(row,col)]; }
T & operator[]( size_t idx ) { return m_data[idx]; }
const T & operator[]( size_t idx ) const { return m_data[idx]; }
// Simple row or column slices
vector<T> Row( int row, int colBegin = 0, int colEnd = -1 ) const;
vector<T> Column( int row, int colBegin = 0, int colEnd = -1 ) const;
private:
vector<T> StridedSlice( int start, int length, int stride ) const;
int m_rows;
int m_cols;
vector<T> m_data;
};
Этот класс в основном покрывает сахар вокруг одной функции — StridedSlice
, Реализация этого:
template <class T>
vector<T> SimpleMatrix<T>::StridedSlice( int start, int length, int stride ) const
{
vector<T> result;
result.reserve( length );
const T *pos = &m_data[start];
for( int i = 0; i < length; i++ ) {
result.push_back(*pos);
pos += stride;
}
return result;
}
А все остальное довольно просто:
template <class T>
SimpleMatrix<T>::SimpleMatrix( int rows, int cols, const T& initVal )
: m_data( rows * cols, initVal )
, m_rows( rows )
, m_cols( cols )
{
}
template <class T>
vector<T> SimpleMatrix<T>::Row( int row, int colBegin, int colEnd ) const
{
if( colEnd < 0 ) colEnd = m_cols-1;
if( colBegin <= colEnd )
return StridedSlice( Index(row,colBegin), colEnd-colBegin+1, 1 );
else
return StridedSlice( Index(row,colBegin), colBegin-colEnd+1, -1 );
}
template <class T>
vector<T> SimpleMatrix<T>::Column( int col, int rowBegin, int rowEnd ) const
{
if( rowEnd < 0 ) rowEnd = m_rows-1;
if( rowBegin <= rowEnd )
return StridedSlice( Index(rowBegin,col), rowEnd-rowBegin+1, m_cols );
else
return StridedSlice( Index(rowBegin,col), rowBegin-rowEnd+1, -m_cols );
}
Обратите внимание, что Row
а также Column
Функции настроены таким образом, что вы можете легко запросить всю строку или столбец, но они немного мощнее, потому что вы можете разделить диапазон, передав один или два дополнительных параметра. И да, вы можете вернуть строку / столбец в обратном порядке, увеличив начальное значение больше конечного.
В эти функции не встроена проверка границ, но вы можете легко это добавить.
Вы также можете добавить что-то, чтобы вернуть срез области как другой SimpleMatrix<T>
,
Повеселись.
Других решений пока нет …