1. 程式人生 > >My Machine Learn(三):c++實現矩陣運算

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進行平行計算

六、完整程式碼和例子

完整的程式碼和例子均在開源中國的 碼雲上,其地址如下:

七、參考與引用

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

相關推薦

My Machine Learn()c++實現矩陣運算

一、背景 機器學習中的神經網路,有人說是模仿人類大腦的神經元,但說白了,其實就是算數運算,單個人工神經元或者神經元層,其權重與輸出,均可以使用矩陣來表示。當然不管是c++還是Python均有矩陣運算的庫(這其中Python的會更多一些),還有GPU加速等版本。

問題五十二怎麼用C++實現矩陣運算

C++程式碼如下: bool matrix_4_4_multiply_4_4(const float matrix1[4][4], const float matrix2[4][4], float (&result)[4][4]) { //求兩個4*4矩陣的乘積 for (int

數值分析(C++實現線性方程組的高斯-賽德爾迭代法

線性方程組的直接解法之後,就輪到迭代解法了,直接解法針對的是低階稠密矩陣,資料量較少,而工程上有更多的是高階係數矩陣,使用迭代法效率更高,佔用的空間較小。 迭代法的最基本思想就是由初始條件,比如說初始解向量隨便列舉一個,就0向量也行,然後進行迭代,k到k+1,一步一步從k=1開始去逼近真實解

數值分析(二)C++實現對角線方程組的追趕法

這次來實現三對角線方程組的追趕法,追趕法的本質還是高斯消元法,而且是沒選主元的高斯消元法,只是因為Ax=b中係數矩陣A非常特殊,所以就可以採用相對特殊的方法來解方程組。同樣,按照常規的步驟,先分析什麼是追趕法,再給出追趕法的數學步驟,最後用C++實現這種演算法。 (一)追趕法的功能和步驟 明

C++實踐(C++實現加密演算法AES

本篇主要講2015年寫的加密演算法。包括:AES,AES-CMAC,HMAC,基於RSA與HMAC的數字簽名演算法。當時大概寫了2天。哈哈! AES演算法 AES是一個對稱加密標準,用以取代DES的商業應用。其分組長度為128位,192位或者256位。

演算法與資料結構基礎9C++實現有向圖——鄰接矩陣儲存

鄰接矩陣的儲存比鄰接表實現起來更加方便,也更加容易理解。 鄰接矩陣就是用一個二維陣列matrix來儲存每兩個點的關係。如果兩個點m,n之間有邊,將陣列matrix[]m[m]設為1,否則設為0。 如果有權,則將matrix[m]n[]設為權值,定義一個很大或者很小的數(只要

【推薦系統實戰】C++實現基於用戶的協同過濾(UserCollaborativeFilter)

color style popu ted std 相似度 abi ear result 好早的時候就打算寫這篇文章,可是還是參加阿裏大數據競賽的第一季三月份的時候實驗就完畢了。硬生生是拖到了十一假期。自己也是醉了。。。找工作不是非常順利,希望寫點東西回想一下知識。然後再

老筆記整理五C實現10階內通過展開代數余子式求行列式的值

實現 num 每一個 oid -1 如何 calc 上線 inpu 這個分為兩部分,先是寫出了C實現計算三階行列式,然後過了一段時間突然有了思路才寫下了10階內這段代碼。真懷念那段寫代碼的日子。 一:C實現計算三階行列式 最近高數課在上線性代數,二階的還能口算,三階的

MySQL()MHA實現MySQL主從架構中主服務器的高可用,zabbix完成manager重啟

code parallel 可以登錄 authorize sudo word systemctl 命令 nag MHA(Master High Availability)是目前在MySQL高可用方面相對成熟的一個解決方案,MHA在監控到master節點故障時,會提升其中擁有

安全不安全002C#實現RSA算法加密解密

RSA C#通過前面的文章我們學會了如何生成公鑰和私鑰,詳見這篇文章:https://blog.csdn.net/yysyangyangyangshan/article/details/80368397。那麽,我們來看在C#中如何實現RSA加密解密。直接上代碼,如下類是RSA算法實現的加密,加解密,簽名以及簽

刁肥宅詳解中綴表示式求值問題C++實現順序/鏈棧解決

       1. 表示式的種類        如何將表示式翻譯成能夠正確求值的指令序列,是語言處理程式要解決的基本問題,作為棧的應用事例,下面介紹表示式的求值過程。 任何一個表示式都是由

安全不安全003C#實現MD5加密演算法

MD5是一種資訊-摘要演算法,一種單向函式演算法(也就是HASH演算法)。將不同輸入長度的資訊進行雜湊計算,得到固定長度的輸出。它的主要特點是,不可逆 和唯一性。即不能由結果計算出輸入值;且不同的輸入值計算得到的固定長度輸出是唯一的。 目前使用的面向物件程式語言中,基本都有類庫實現好的MD5方法

資料結構實驗3C++實現順序棧類與鏈棧類

                          

資料結構實驗4C++實現迴圈佇列

實驗4 4.1 實驗目的 熟練掌握佇列的順序儲存結構和鏈式儲存結構。 熟練掌握佇列的有關演算法設計,並在迴圈順序佇列和鏈佇列上實現。 根據具體給定的需求,合理設計並實現相關結構和演算法。 4.2 實驗要求 4.2.1 迴圈順序佇列的實驗要求 迴圈順序佇列結構和運算定義,演算法的實現以庫檔案方式實

C實現矩陣運算

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

資料結構實驗6C++實現二叉樹類

實驗6 學號:     姓名:      專業:   6.1 實驗目的 掌握二叉樹的動態連結串列儲存結構及表示。 掌握二叉樹的三種遍歷演算法(遞迴和非遞迴兩類)。 運用二叉樹三種遍歷的方法求解有關問題。 6

Kafka訊息佇列介紹、環境搭建及應用C#實現消費者-生產者訂閱

一:kafka介紹 kafka(官網地址:http://kafka.apache.org)是一種高吞吐量的分散式釋出訂閱的訊息佇列系統,具有高效能和高吞吐率。 1.1 術語介紹 Broker Kafka叢集包含一個或多個伺服器,這種伺服器被稱為broker

C++實現棧(stack)資料結構

本文使用C++實現棧資料結構,棧資料結構同之前實現的vector和list資料結構一樣都屬於線性序列結構,但是棧的資料操作範圍僅限於邏輯上的特定頂端,即只能對棧頂進行操作。由於棧結構具有的簡潔性和規範性,它既為構建更復雜、更高階資料結構的基礎,也是演算法設計的基本出發點。鑑於其的基礎性及使用的頻

總結python實現矩陣最基本應用

矩陣在機器學習中算比較常見的,小記一筆最基本的東西。 #-*-coding:utf-8-*- import numpy as np a = np.array([[1,2],[3,4]]) print a b = np.array([[4,5],[6,7]]) p

C語言程式設計現代方法(第2版)(K.N.King 著)》學習筆記C語言基本概念(2)

2.3 註釋 每一個程式都應該包含識別資訊,即程式名、編寫日期、作者、程式的用途以及其他相關資訊。C語言把這類資訊放在註釋(comment)中。 符號 /* 標記註釋的開始,而符號 */ 則標記註釋