My Machine Learn(三):c++实现矩阵运算

一、背景

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

六、完整代码和例子

完整的代码和例子均在开源中国的 码云上,其地址如下:

https://gitee.com/xunawolanxue/my_demo_code/tree/master/My_Machine_Learn/c++实现矩阵运算/version1

七、参考与引用

本文代码的实现部分,参考了clangpp 的实现版本,在此基础上,做了精简,优化。

其CSDN地址:https://blog.csdn.net/clangpp/article/details/38884953

其代码的GitHub地址为: https://github.com/clangpp/codeset/tree/master/matrix/src/matrix

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值