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.com/my_demo_code/tree/master/My_Machine_Learn/c++%E5%AE%9E%E7%8E%B0%E7%9F%A9%E9%98%B5%E8%BF%90%E7%AE%97/version1

七、參考與引用

本文代碼的實現部分,參考了clangpp 的實現版本,在此基礎上,做了精簡,優化。

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

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章