一、背景
机器学习中的神经网络,有人说是模仿人类大脑的神经元,但说白了,其实就是算数运算,单个人工神经元或者神经元层,其权重与输出,均可以使用矩阵来表示。当然不管是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