一、背景
機器學習中的神經網絡,有人說是模仿人類大腦的神經元,但說白了,其實就是算數運算,單個人工神經元或者神經元層,其權重與輸出,均可以使用矩陣來表示。當然不管是c++還是Python均有矩陣運算的庫(這其中Python的會更多一些),還有GPU加速等版本。
這裏我想實現一個c++版本,用以實現簡單的全連接神經網絡,起重點是簡單,高效,不在乎要實現多複雜的功能。
二、矩陣類定義
這裏以模板的方式來實現矩陣類,具體如下:
template<typename T>
class Matrix
{
public:
typedef T value_type; /**矩陣中元素的數據類型 */
typedef T& value_ref; /** 矩陣中元素的引用數據類型 */
typedef const T& const_value_ref; /** 矩陣中元素的常引用數據類型 */
typedef T* value_ptr; /** /** 矩陣中元素的指針數據類型 */*/
typedef std::size_t size_type;
typedef T* row_type; /** 表示矩陣一行的數據類型別名 */
typedef const T* const_row_type;/** 表示矩陣一行的常量數據類型別名 */
/**初始化列表數據類型,用於類似如下方式初始化矩陣的情況
* Matrix<int> a =
* {
* { 1, 2 },
* { 3, 4 },
* };
*
**/
typedef std::initializer_list<std::initializer_list<value_type>>
value_list_type;
typedef row_type* data_type; /** 存放矩陣數據的二維數組(或指針) */
/**first: row size, second: column size */
typedef std::pair<size_type, size_type> shape_type;/**類似Python中numpy的shape結構 */
/** 進行元素對比的回調函數 */
typedef std::function<bool(const value_type&, const value_type&)>
value_compare_func;
public:
/** Constructors and Destructor */
Matrix(size_type row_size, size_type column_size,
const value_type& filled = value_type());
Matrix(value_list_type il);
Matrix();
Matrix(const Matrix& other);
Matrix(Matrix&& other);
~Matrix();
public:
/** size and shape */
size_type row_size() const; /** 獲取有多少行 */
size_type column_size() const; /** 獲取有多少列 */
size_type size() const; /** 獲取矩陣中總共有多少個元素 */
shape_type shape() const; /**獲取矩陣的形狀(或者維度) */
bool empty() const; /** 判斷矩陣是否爲空, 空:true, 非空:false */
public:
/** Assignment operator */
Matrix & operator=(const Matrix& other);
Matrix& operator=(Matrix&& other);
public:
/** get value */
value_ref operator()(size_type row, size_type column);
const_value_ref operator()(size_type row, size_type column) const;
/** Supports access pattern `mat[row][column]`. **/
row_type operator[](size_type row);
const_row_type operator[](size_type row) const;
public:
/** 調整矩陣的行列大小 */
void row_resize(size_type new_row_size,
const value_type& filled = value_type());
void column_resize(size_type new_column_size,
const value_type& filled = value_type());
void reshape(size_type row_size, size_type column_size,
const value_type& filled = value_type());
public:
/** Arithmetric operators */
Matrix& operator+=(const Matrix& other); /** add */
Matrix& operator-=(const Matrix& other); /** minus */
/** multiply */
Matrix& operator*=(const value_type& scaler);
Matrix& operator*=(const Matrix& other);
friend Matrix operator*(const Matrix& lhs, Matrix&& rhs)
{
// matrix::CheckShapeMultipliable(lhs, rhs);
Matrix<T> result(lhs);
result *= rhs; /** todo refine me?? */
return result;
}
/** Each element corresponds to multiply */
Matrix& multiply(const Matrix& other);
Matrix& operator/=(const value_type& scaler); /** div */
Matrix& operator%=(const value_type& scaler); /** mod */
/** compare */
bool compare(const Matrix& other,
value_compare_func value_compare =
std::equal_to<value_type>()) const;
private:
/** check functions */
void checkColumnSizesEqual(value_list_type il);/**檢查初始列表的每一行的列數是否相等 */
void checkShapeRange(size_type row, size_type column) const;/**檢查行列值是否超出範圍 */
void checkShapeMatches(const Matrix<T>& other);/** 檢查目標矩陣和當前矩陣的行列是否相等 */
/** for process data pointer */
/**初始化數據,分配相應內存 */
void initializeData(size_type rowSize, size_type columnSize);
/**初始化數據,分配內存,並以特定值填充整個矩陣 */
void initializeData(size_type rowSize, size_type columnSize,
const value_type& filled);
/** 反初始化數據,並重置矩陣行列大小 */
void uninitializeData();
/** 釋放矩陣元素的數據結構的內存 */
void releaseData(data_type data);
/** 乘積累加,用於矩陣乘法 */
value_type inner_product(value_type* beg1, value_type* end1,
data_type beg2, size_type column2,
value_type init);
private:
data_type mData = nullptr; /** 矩陣元素數據 */
size_type mRowSize = 0; /** 矩陣行大小 */
size_type mColumnSize = 0; /** 矩陣列大小 */
};
三、矩陣類實現
爲了讓矩陣模板類的聲明看起來很清晰簡潔,所以絕大多數函數都是在模板類外部實現的。
3.1 構造函數和析構函數
- 首先是實現矩陣類的構造函數:
/** 根據行列值構造矩陣對象,並以特定初始值填充 */
template<typename T>
Matrix<T>::Matrix(size_type row_size, size_type column_size,
const value_type& filled /*= value_type()*/)
{
if (row_size && column_size)
{
initializeData(row_size, column_size, filled);
}
}
/** 使用初始化列表構造矩陣對象,形如:
* Matrix<int> a =
* {
* { 1, 2 },
* { 3, 4 },
* };
*/
template<typename T>
Matrix<T>::Matrix(value_list_type il)
{
if (il.size() > 0)
{
checkColumnSizesEqual(il);
initializeData(il.size(), il.begin()->size());
size_type rowIdx = 0;
size_type columnIdx = 0;
for (auto row_il : il)
{
columnIdx = 0;
for (auto val : row_il)
{
mData[rowIdx][columnIdx] = val;
columnIdx++;
}
rowIdx++;
}
}
}
/** 默認構造函數 */
template<typename T>
Matrix<T>::Matrix()
{
/**todo something */
}
/** 拷貝構造函數 */
template<typename T>
Matrix<T>::Matrix(const Matrix& other)
{
if (other.mRowSize && other.mColumnSize && other.mData)
{
initializeData(other.mRowSize, other.mColumnSize);
memcpy(mData[0], other.mData[0], mRowSize * mColumnSize * sizeof(value_type));
}
}
/** 帶移動語義的拷貝構造函數 */
template<typename T>
Matrix<T>::Matrix(Matrix&& other)
{
if (other.mRowSize && other.mColumnSize && other.mData)
{
mRowSize = other.mRowSize;
mColumnSize = other.mColumnSize;
mData = other.mData;
other.mRowSize = 0;
other.mColumnSize = 0;
other.mData = nullptr;
}
}
- 其次是析構函數
template<typename T>
Matrix<T>::~Matrix()
{
uninitializeData();
}
3.2 獲取矩陣信息
這裏是獲取矩陣信息的接口實現,比如行大小,列大小,是否爲空等,具體實現如下:
template<typename T>
typename Matrix<T>::size_type Matrix<T>::row_size() const
{/** 獲取行大小 */
return mRowSize;
}
template<typename T>
typename Matrix<T>::size_type Matrix<T>::column_size() const
{/** 獲取列大小 */
return mColumnSize;
}
template<typename T>
typename Matrix<T>::size_type Matrix<T>::size() const
{/** 獲取矩陣中總共有多少個元素 */
return mRowSize * mColumnSize;
}
template<typename T>
typename Matrix<T>::shape_type Matrix<T>::shape() const
{/** 獲取矩陣的形狀(類似Python中numpy中的shape) */
return std::make_pair(mRowSize, mColumnSize);
}
template<typename T>
bool Matrix<T>::empty() const
{/** 判斷矩陣是否爲空 */
return (mRowSize == 0) || (mColumnSize == 0);
}
3.3 賦值符號重載
這裏是賦值符號的重載, 有兩種,其一,普通賦值,其二,帶移動語義的賦值,其實現如下:
template<typename T>
Matrix<T>& Matrix<T>::operator=(const Matrix<T>& other)
{
if (this != &other)
{
if (other.mRowSize && other.mColumnSize && other.mData)
{
if ((mRowSize != other.mRowSize) ||
(mColumnSize != other.mColumnSize))
{/**如果目標矩陣和當前矩陣的行列大小不相等,此時就需要給當前矩陣重新分配內存 */
uninitializeData();
initializeData(other.mRowSize, other.mColumnSize);
}
/** 拷貝矩陣數據 */
memcpy(mData[0], other.mData[0],
mRowSize * mColumnSize * sizeof(value_type));
}
else
{/** 如果目標矩陣爲空,則重置當前矩陣 */
uninitializeData();
}
}
return *this;
}
template<typename T>
Matrix<T>& Matrix<T>::operator=(Matrix<T>&& other)
{
if (this != &other)
{/** 帶移動語義的賦值, 只需將目標矩陣的數據指針賦值給當前矩陣,然後置空目標矩陣即可,很快速 */
mRowSize = other.mRowSize;
mColumnSize = other.mColumnSize;
mData = other.mData;
other.mRowSize = 0;
other.mColumnSize = 0;
other.mData = nullptr;
}
return *this;
}
3.4 獲取矩陣元素的值
這裏是通過重載“()” 和“[]”兩個符號了實現的,其具體代碼如下:
template<typename T>
typename Matrix<T>::value_ref Matrix<T>::operator()(size_type row,
size_type column)
{/** 普通引用,可以作爲右值,也可以作爲左值 */
checkShapeRange(row, column); /**檢查行列索引值是否超出範圍,如果超出則拋出異常 */
return mData[row][column];
}
template<typename T>
typename Matrix<T>::const_value_ref Matrix<T>::operator()(size_type row,
size_type column) const
{/** 作爲常量引用,只能作爲右值 */
checkShapeRange(row, column);/**檢查行列索引值是否超出範圍,如果超出則拋出異常 */
return mData[row][column];
}
/** Supports access pattern `mat[row][column]`. **/
template<typename T>
typename Matrix<T>::row_type Matrix<T>::operator[](size_type row)
{/**對括號重載,這裏是以指針的形式實現的,其目標類只能實現一個"[]"的重載,另一個"[]"
* 這裏是通過指針取值實現的,這樣這裏就不會對行列的索引值進行判斷,需注意
*/
return mData[row];
}
template<typename T>
typename Matrix<T>::const_row_type Matrix<T>::operator[](
size_type row) const
{/**對括號重載,這裏是以指針的形式實現的,其目標類只能實現一個"[]"的重載,另一個"[]"
* 這裏是通過指針取值實現的,這樣這裏就不會對行列的索引值進行判斷,需注意
*/
return mData[row];
}
3.5 動態調整矩陣大小
這裏實現了三種調整矩陣大小的方法,調整行大小,調整列大小,同時調整行和列的大小。 其具體實現如下:
template<typename T>
void Matrix<T>::row_resize(size_type new_row_size,
const value_type& filled /*= value_type()*/)
{/**調整行大小,如果行變大,超出原始大小部分,均爲“filled”值填充 */
if (new_row_size <= mRowSize)
{
mRowSize = new_row_size;
}
else
{
data_type orgData = mData;
size_type orgSize = mRowSize * mColumnSize ;
initializeData(new_row_size, mColumnSize);
memcpy(mData[0], orgData[0], orgSize * sizeof(value_type));
size_type newSize = mRowSize * mColumnSize;
value_type* pData = mData[0] + orgSize;
for (size_t i = 0; i < newSize - orgSize; i++)
{
pData[i] = filled;
}
releaseData(orgData);
}
}
template<typename T>
void Matrix<T>::column_resize(size_type new_column_size,
const value_type& filled /*= value_type()*/)
{/**調整列大小,如果列變大,超出原始大小部分,均爲“filled”值填充 */
if (new_column_size <= mColumnSize)
{
mColumnSize = new_column_size;
}
else
{
data_type orgData = mData;
size_type orgColumnSize = mColumnSize;
initializeData(mRowSize, new_column_size);
for (size_t row = 0; row < mRowSize; row++)
{
for (size_t column = 0; column < mColumnSize; column++)
{
if (column < orgColumnSize)
{
mData[row][column] = orgData[row][column];
}
else
{
mData[row][column] = filled;
}
}
}
releaseData(orgData);
}
}
template<typename T>
void Matrix<T>::reshape(size_type row_size, size_type column_size,
const value_type& filled /*= value_type()*/)
{/**同時調整行和列,類似Python中numpy中的reshape,如果行或列變大,
* 超出原始大小部分,均爲“filled”值填充
*/
if ((row_size <= mRowSize) && (column_size <= mColumnSize))
{
mRowSize = row_size;
mColumnSize = column_size;
}
else if((row_size == mRowSize) && (column_size != mColumnSize))
{
column_resize(column_size, filled);
}
else if ((column_size == mColumnSize) && (row_size != mRowSize))
{
row_resize(row_size, filled);
}
else
{/** row and column both bigger then old size */
data_type orgData = mData;
size_type orgRowSize = mRowSize;
size_type orgColumnSize = mColumnSize;
initializeData(row_size, column_size);
for (size_t i = 0; i < mRowSize; i++)
{
if (i < orgRowSize)
{
for (size_t j = 0; j < mColumnSize; j++)
{
if (j < orgColumnSize)
{
mData[i][i] = orgData[i][j];
}
else
{
mData[i][j] = filled;
}
}
}
else
{
for (size_t j = 0; j < mColumnSize; j++)
{
mData[i][j] = filled;
}
}
}
releaseData(orgData);
}
}
3.6 算術運算
這裏就是矩陣運算的重點了,這一單元實現了包括加、減、乘、除以及取餘等運算。其中乘法有實現了多種形式的
- 對矩陣所有元素乘以一個值,達到矩陣元素值“縮放”的效果
- 矩陣乘法的內積,如A * B, 就是將A矩陣的每一行和B矩陣中的每一列的元素相乘並累加(也就是說A的行與B的列必須相等)
- 和加減法類似,兩個矩陣的每個元素分別相乘,也就是說矩陣A和矩陣B的行和列都必須相等,和加減法的判斷條件一致
3.6.1 加減法
首先來看最基本的加減法的實現,其代碼如下:
template<typename T>
Matrix<T>& Matrix<T>::operator+=(const Matrix& other) /** add */
{
matrix::CheckShapeMatches(*this, other);/** 檢查兩個矩陣的行列(或shape)是否相等 */
data_type otherData = other.mData;
for (size_t row = 0; row < mRowSize; row++)
{
for (size_t column = 0; column < mColumnSize; column++)
{
mData[row][column] += otherData[row][column];
}
}
return *this;
}
template<typename T>
Matrix<T>& Matrix<T>::operator-=(const Matrix& other) /** minus */
{
matrix::CheckShapeMatches(*this, other);/** 檢查兩個矩陣的行列(或shape)是否相等 */
data_type otherData = other.mData;
for (size_t row = 0; row < mRowSize; row++)
{
for (size_t column = 0; column < mColumnSize; column++)
{
mData[row][column] -= otherData[row][column];
}
}
return *this;
}
3.6.2 乘法、除法以及取餘
先看三種不同的乘法實現,也是矩陣運算在機器學習者最常用到,最算法和速度都有影響的運算:
template<typename T>
Matrix<T>& Matrix<T>::operator*=(const value_type& scaler)
{/** “縮放”形式的乘法 */
for (size_t row = 0; row < mRowSize; row++)
{
for (size_t column = 0; column < mColumnSize; column++)
{
mData[row][column] *= scaler;
}
}
return *this;
}
template<typename T>
Matrix<T>& Matrix<T>::operator*=(const Matrix& other)
{/** 求內積的乘法 */
matrix::CheckShapeMultipliable(*this, other);/**檢查兩個矩陣是否滿足乘法條件 */
data_type orgData = mData;
size_type orgColumnSize = mColumnSize;
initializeData(mRowSize, other.mColumnSize);
for (size_t row = 0; row < mRowSize; row++)
{
for (size_t column = 0; column < mColumnSize; column++)
{
mData[row][column] = inner_product(&(orgData[row][0]),
&(orgData[row][orgColumnSize]),
other.mData, column, value_type());
}
}
releaseData(orgData);
return *this;
}
/** Each element corresponds to multiply */
template<typename T>
Matrix<T>& Matrix<T>::multiply(const Matrix<T>& other)
{/** 兩個矩陣的每個元素交叉相乘 */
matrix::CheckShapeMatches(*this, other);/**檢查兩個矩陣行列是否相等 */
data_type otherData = other.mData;
for (size_t row = 0; row < mRowSize; row++)
{
for (size_t column = 0; column < mColumnSize; column++)
{
mData[row][column] *= otherData[row][column];
}
}
return *this;
}
然後就是除法和取餘:
template<typename T>
Matrix<T>& Matrix<T>::operator/=(const value_type& scaler) /** div */
{/**除法,與“縮放”的乘法類似,注:這裏沒有判斷scaler是否爲零,爲零將自動拋出除零異常 */
for (size_t row = 0; row < mRowSize; row++)
{
for (size_t column = 0; column < mColumnSize; column++)
{
mData[row][column] /= scaler;
}
}
return *this;
}
template<typename T>
Matrix<T>& Matrix<T>::operator%=(const value_type& scaler) /** mod */
{/** 矩陣的每個元素依次被scaler取餘 */
for (size_t row = 0; row < mRowSize; row++)
{
for (size_t column = 0; column < mColumnSize; column++)
{
mData[row][column] %= scaler;
}
}
return *this;
}
3.7 比較函數
最後一個是比較函數,用於對比兩個矩陣的元素是否完全相等,相等返回true, 否則返回false:
template<typename T>
bool Matrix<T>::compare(const Matrix<T>& other,
value_compare_func value_compare
/* = std::equal_to<value_type>()*/) const
{/**比較函數默認使用c++標準庫中的equal_to模板 */
bool ret = false;
if ((mRowSize == other.mRowSize) && (mColumnSize == other.mColumnSize))
{
ret = true;
data_type otherData = other.mData;
for (size_t row = 0; row < mRowSize; row++)
{
for (size_t column = 0; column < mColumnSize; column++)
{
if (!value_compare(mData[row][column], otherData[row][column]))
{/**使用傳入的對比函數,比較兩個矩陣的中相同行列處的元素是否相等 */
ret = false;
break;
}
}
}
}
return ret;
}
四、簡單例子
這裏羅列幾個使用矩陣模板類的簡單例子。
4.1 構造矩陣對象
這裏就簡單舉兩個構造例子,其他的有興趣的讀者可自行嘗試,或者參考完整代碼中的測試樣例
Matrix<int> a(2, 4, 3); /** 構建一個2行4列的整型矩陣,並將全部元素填充爲3 */
for (int i = 0; i < 2; ++i) /** 對構建結構進行檢查 */
{
for (int j = 0; j < 4; ++j)
{
assert(a(i, j) == 3);
}
}
Matrix<int> b(3, 5); /** 構建一個3行5列的整型矩陣,並以默認的0填充 */
for (int i = 0; i < 3; ++i)
{
for (int j = 0; j < 5; ++j)
{
assert(b(i, j) == 0);
}
}
4.2 調整矩陣大小
這裏就分別列出調整矩陣行,調整矩陣列以及同時調整矩陣行列的例子。
Matrix<int> a(3, 5, 3); /** 分配3行5列,並將元素初始化爲3 */
assert(a.row_size() == 3); /** 檢查行列信息 */
assert(a.column_size() == 5);
for (int i = 0; i < 5; ++i) /** 檢查元素值 */
{
for (int j = 0; j < 5; ++j)
{
assert(a[i][j] == 3);
}
}
/** 調整行大小 */
a.row_resize(4, 30); /** 將行調整爲4, 新增元素初始化爲30 */
assert(a.row_size() == 4); /** 檢查行列信息 */
assert(a.column_size() == 5);
for (int i = 0; i < 4; ++i) /** 檢查元素值 */
{
for (int j = 0; j < 5; ++j)
{
assert(a[i][j] == (i<3 ? 3 : 30));
}
}
/** 調整列大小 */
a.column_resize(7, 70);/** 將列調整爲7, 新增元素初始化爲70 */
assert(a.row_size() == 4);
assert(a.column_size() == 7);
for (int i = 0; i < 4; ++i)
{
for (int j = 0; j < 7; ++j)
{
assert(a[i][j] == (j < 5 ? 3 : 70));
}
}
/** 同時調整行列 */
a.reshape(2,3); /** 同時調整行列爲 2行3列 */
assert(a.row_size() == 2); /** 檢查行列信息 */
assert(a.column_size() == 3);
for (int i = 0; i < 2; ++i) /** 檢查元素值 */
{
for (int j = 0; j < 3; ++j)
{
assert(a[i][j] == 3);
}
}
4.3 算術運算
這裏的算術運算和普通的數字使用算術符號進行算術運算差不多。
- 加法
Matrix<int> a(3, 4, 10), b(3, 4, 20);/** 構建兩個行和列都相等的矩陣,並初始化爲不同的值 */
a += b; /** 矩陣a和b累加,並將累加結果賦值給矩陣a */
assert(a.row_size() == 3); /** 檢查a矩陣的行列信息系 */
assert(a.column_size() == 4);
for (int i = 0; i < 3; ++i) /** 檢查累加後矩陣a的矩陣元素值 */
{
for (int j = 0; j < 4; ++j)
{
assert(a[i][j] == 30);
}
}
Matrix<int> d = a + b; /** 將矩陣a和b累加,並賦值給矩陣d */
assert(d.row_size() == 3); /** 檢查矩陣d的行列信息 */
assert(d.column_size() == 4);
for (int i = 0; i < 3; ++i) /** 檢查矩陣d的元素值 */
{
for (int j = 0; j < 4; ++j)
{
assert(d[i][j] == 50);
}
}
- 減法
/**構建3個行列相等的矩陣,其中矩陣c是拷貝矩陣a進行構造 */
Matrix<int> a(3, 4, 10), b(3, 4, 20), c(a);
c -= b; /** 矩陣c減去矩陣b,並將結果賦值給矩陣c */
assert(c.row_size() == 3); /** 判斷矩陣c的行列信息 */
assert(c.column_size() == 4);
for (int i = 0; i < 3; ++i) /** 檢查矩陣c的元素值 */
{
for (int j = 0; j < 4; ++j)
{
assert(c[i][j] == -10);
}
}
Matrix<int> d = a - b; /** 矩陣a減去矩陣b,並賦值給矩陣d*/
assert(d.row_size() == 3); /**檢查矩陣d的行列信息*/
assert(d.column_size() == 4);
for (int i = 0; i < 3; ++i) /** 檢查矩陣d的元素值 */
{
for (int j = 0; j < 4; ++j)
{
assert(d[i][j] == -10);
}
}
- 乘法
Matrix<int> c(3, 4, 10); /** "縮放"類型的乘法 */
c *= 3; /** 矩陣c的所有元素都乘以3 */
assert(c.row_size() == 3); /** 檢查矩陣c的行列信息 */
assert(c.column_size() == 4);
for (int i = 0; i < 3; ++i) /** 檢查矩陣c的元素值 */
{
for (int j = 0; j < 4; ++j)
{
assert(c[i][j] == 30);
}
}
Matrix<int> a(3, 4, 10), b(4, 5, 20);
Matrix<int> d = a * b;/**求內積乘法,並賦值給矩陣d */
assert(d.row_size() == 3); /** 檢查矩陣d的行列信息,正確值爲: 矩陣a的行,矩陣b的列 */
assert(d.column_size() == 5);
for (int i = 0; i < 3; ++i) /** 檢查矩陣d的元素值 */
{
for (int j = 0; j < 5; ++j)
{
assert(d[i][j] == 800);
}
}
Matrix<int> e(3, 4, 10), f(3, 4, 20);
e.multiply(f)/** 兩個矩陣的每個元素交叉相乘,並賦值給矩陣e */
assert(e.row_size() == 3);/** 檢查矩陣e的行列信息,正確值是:不變 */
assert(e.column_size() == 5);
for (int i = 0; i < 3; ++i) /** 檢查矩陣e的元素值 */
{
for (int j = 0; j < 4; ++j)
{
assert(e[i][j] == 200);
}
}
- 除法
Matrix<int> a(3, 4, 10), c(a);
c /= 2;/** 矩陣的所有元素都除以2 並賦值給矩陣c */
assert(c.row_size() == 3); /** 檢查矩陣c的行列信息,正確值是:不變 */
assert(c.column_size() == 4);
for (int i = 0; i < 3; ++i) /** 檢查矩陣c的元素值 */
{
for (int j = 0; j < 4; ++j)
{
assert(c[i][j] == 5);
}
}
Matrix<int> d = a / 2; /** 矩陣a的所有元素除以2,並賦值給矩陣d */
assert(d.row_size() == 3); /** 檢查矩陣d的行列信息 */
assert(d.column_size() == 4);
for (int i = 0; i < 3; ++i) /** 檢查矩陣d的元素值 */
{
for (int j = 0; j < 4; ++j)
{
assert(d[i][j] == 5);
}
}
- 取餘
Matrix<int> a(3, 4, 10), c(a);
c %= 3;/**矩陣c中的所有元素被3取餘,並賦值給矩陣c */
assert(c.row_size() == 3);/** 判斷矩陣c的行列信息 */
assert(c.column_size() == 4);
for (int i = 0; i < 3; ++i) /** 檢查矩陣c的元素值 */
{
for (int j = 0; j < 4; ++j)
{
assert(c[i][j] == 1);
}
}
Matrix<int> d = a % 4; /** 矩陣a的所有元素被4取餘,並賦值給矩陣d */
assert(d.row_size() == 3);/**檢查矩陣d的行列信息 */
assert(d.column_size() == 4);
for (int i = 0; i < 3; ++i) /** 檢查矩陣d的元素值 */
{
for (int j = 0; j < 4; ++j)
{
assert(d[i][j] == 2);
}
}
五、後續可優化的點
目前這個版本只是最基礎的實現,後續還可進行如下優化(可能不止這些優化方案):
- 如加減運算等,可以在運算前判斷行列值的大小,小的一個作爲外層循環,大的作爲內層循環
- 給計算添加線程,比如將每一行的運算都丟到一個線程中去運算,然後再綜合運算結果
- 對特定平臺使用匯編代碼或特別的運算指令集進行優化
- 使用GPU進行並行計算
六、完整代碼和例子
完整的代碼和例子均在開源中國的 碼雲上,其地址如下:
七、參考與引用
本文代碼的實現部分,參考了clangpp 的實現版本,在此基礎上,做了精簡,優化。
其CSDN地址:https://blog.csdn.net/clangpp/article/details/38884953
其代碼的GitHub地址爲: https://github.com/clangpp/codeset/tree/master/matrix/src/matrix