1. 程式人生 > >矩陣基礎演算法實現

矩陣基礎演算法實現

       最近花了一段時間去溫習矩陣的基礎演算法,要去敲程式碼的時候才發現原來以前的線性程式碼全都白學了,沒有去用沒有去實現的結果就等於什麼都沒用。

       自己為了練習實現了一些矩陣的基礎演算法,算是一個溫習,一條條羅列下來。其中我的矩陣資料為浮點數格式,0這樣的精度判斷上(+/-0.000001f之間)可能還有些侷限,僅供學習參考,如果發現跑不通的用例也歡迎及時反饋給我。          首先是矩陣基本格式的定義,比較簡單。

<pre name="code" class="csharp">public class Matrix
{
    public const float MAX = 10000000f;
    public const float ZERO = 0.000001f; //極小值,認為是0
    //空物件
    private Matrix NULL = null;


    public Dictionary<int, List<float>> data = new Dictionary<int, List<float>>();
    public Matrix()
    {
    }

    /// <summary>
    /// 帶參構造:float二維陣列構造一個矩陣
    /// ex:float[,] data = new float[,] { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };
    /// </summary>
    /// <param name="data"></param>
    public Matrix(float[,] dataArray)
    {
        for (int index = 0; index < dataArray.GetLength(0); index++)
        {
            List<float> list = new List<float>();
            for (int subindex = 0; subindex < dataArray.GetLength(0); subindex++)
            {
                list.Add(dataArray[index, subindex]);
            }
            this.data.Add(index, list);
        }
    }
}

如上是矩陣的預設預設構造和基本建構函式,傳入的資料格式為一個二維的浮點矩陣。先來介紹具體的實現:

(1)矩陣相乘

矩陣A和B相乘的基本思路(要滿足A行等於B列的條件):結果矩陣的i行等於A的i行所有元素和B的i列所有元素依次乘積的和。結果矩陣的行列就為A行B列。開始寫的是個矩陣類的成員函式,後來覺得這樣很不好用,還是過載了*運算,可以使矩陣直接相乘運算matrest=mata*matb這樣:

  /// <summary>
    /// 過載矩陣乘法*,
    /// </summary>
    /// <param name="matrixA"></param>
    /// <param name="matrixB"></param>
    /// <returns></returns>
    public static Matrix operator *(Matrix matrixA, Matrix matrixB)
    {
        Matrix resMatrix = new Matrix();
        List<int> keyList = new List<int>(matrixA.data.Keys);
        Dictionary<int, List<float>> dic = matrixA.data;//[keyList[inIndex]];
        //現在是A矩陣的inIndex行,要分別乘以B的inIndex列
        //遍歷沒一列
        for (int lineIndex = 0; lineIndex < matrixA.LieCount; lineIndex++)
        {
            List<float> resList = new List<float>();
            //即將遍歷每一行
            for (int rowIndex = 0; rowIndex < matrixA.HangCount; rowIndex++)
            {
                float rowNum = 0;
                //將A行rowIndex行subIndex列的數字和B中rowIndex列subIndex行的數字相乘
                List<float> aRow = matrixA.GetHang(lineIndex);  //A行的資料
                List<float> bLine = matrixB.GetLie(rowIndex);//B列的資料
                for (int subIndex = 0; subIndex < aRow.Count; subIndex++)
                {
                    rowNum += aRow[subIndex] * bLine[subIndex];
                }
                resList.Add(rowNum);
            }
            resMatrix.data.Add(lineIndex, resList);
        }
        return resMatrix;
    }

(2)矩陣相加:

矩陣A+B相對簡單,直接每個元素相加的結果即可,需要保證A與B的行列都相同:

    /// <summary>
    /// 過載+號,計算矩陣相加
    /// </summary>
    /// <param name="lMat"></param>
    /// <param name="rmat"></param>
    /// <returns></returns>
    public static Matrix operator +(Matrix lMat, Matrix rMat)
    {
        if (lMat.HangCount != rMat.HangCount)
        {
            return null;
        }
        if (lMat.LieCount != rMat.LieCount)
        {
            return null;
        }
        //計算 矩陣相加
        Matrix resMat = new Matrix();
        List<int> keyList = new List<int>(lMat.data.Keys);
        for (int index = 0; index < keyList.Count; index++)
        {
            List<float> lines = lMat.data[keyList[index]];
            List<float> linesB = rMat.data[keyList[index]];
            List<float> resList = new List<float>();
            for (int subIndex = 0; subIndex < lines.Count; subIndex++)
            {
                resList.Add(lines[subIndex] + linesB[subIndex]);
            }
            resMat.data.Add(index, resList);
        }
        return resMat;
    }

(3)求矩陣的模,矩陣的模僅針對方針討論,同時2階矩陣的處理有別於多階矩陣,注意點都註明了,同時overflow欄位的結果為true的話也就表明模也就失效,表示不是矩陣:
    /// <summary>
    /// 計算矩陣的模
    /// </summary>
    /// <returns></returns>
    public float GetModule(out bool overflow)
    {
        overflow = false;
        //行數不等於列數,
        if (LieCount != HangCount)
        {
            overflow = true;
            return 0;
        }

        //2階矩陣的模為0 啊
        ///a b
        ///c d
        ///模= a*d - c*b
        if (LieCount == 2)
        {
            return getMultioflist(getRightDownList(0)) - getMultioflist(getLeftUpList(0));
        }

        //測試通過
        List<float> rightDown = new List<float>();//右下各個列的積

        for (int index = 0; index < LieCount; index++)
        {
            rightDown.Add(getMultioflist(getRightDownList(index)));
        }

        List<float> leftUp = new List<float>();
        for (int index = 0; index < LieCount; index++)
        {
            leftUp.Add(getMultioflist(getLeftUpList(index)));
        }

        float result = getSumofList(rightDown) - getSumofList(leftUp);
        return result;
    }

(4)求矩陣的轉置:

矩陣的轉置的結果就是原來的i行變成了i列:

  /// <summary>
    /// 獲得轉置矩陣,不改變原矩陣
    /// </summary>
    /// <returns></returns>
    public Matrix GetTranspose()
    {
        //非方陣
        if (HangCount != LieCount)
        {
            return null;
        }
        Matrix transpose = new Matrix();
        List<int> keyList = new List<int>(data.Keys);
        for (int index = 0; index < keyList.Count; index++)
        {
            List<float> hang = new List<float>(data[keyList[index]]);
            for (int subIndex = 0; subIndex < hang.Count; subIndex++)
            {
                if (transpose.data.ContainsKey(subIndex))
                {
                    transpose.data[subIndex].Add(hang[subIndex]);
                }
                else
                {
                    transpose.data.Add(subIndex, new List<float> { hang[subIndex] });
                }
            }
        }
        return transpose;
    }

(5)求矩陣的標準伴隨矩陣:

矩陣A的標準伴隨矩陣,就是A的代數餘子式矩陣(所有行列的代數餘子式的模組成的矩陣)轉置得到的,得到標準伴隨矩陣的方法為 GetStanderedAdjoint(),最重要的還是求標準伴隨矩陣的方法。

其中i行j列的代數餘子式的矩陣其實是原矩陣A去掉i行j列的結果矩陣,而代數餘子式矩陣就是由這些餘子式的模*符號sign 組成的矩陣。具體符號sign的值為1要求i+j為偶數,否則sign為-1。

   //A的標準伴隨矩陣
    public Matrix GetStanderedAdjoint()
    {
        return GetConfactorMat().GetTranspose();
    }

    //獲取代數餘子式組成的矩陣
    public Matrix GetConfactorMat()
    {
        Matrix resMat = new Matrix();
        for (int hangInd = 0; hangInd < HangCount; hangInd++)
        {
            List<float> list = GetHang(hangInd);
            List<float> resList = new List<float>();
            for (int lieInd = 0; lieInd < list.Count; lieInd++)
            {
                //獲取index行,subindex列的代數餘子式的模啊
                float sign = (hangInd + lieInd) % 2 == 0 ? 1f : -1f;//結果行列的符號
                float mode = GetConfactor(hangInd, lieInd).Mode; //.Mode為矩陣的模
                resList.Add(sign * mode);
            }
            resMat.data.Add(hangInd, resList);
        }
        return resMat;
    }

(6)求矩陣的逆矩陣,都不用多說,程式碼中都已經有詳細的演算法註明。除了MultiScalar(float)方法是沒有粘出來的矩陣乘以浮點數。
  /// <summary>
    /// 獲得一個矩陣的逆矩陣:如果是,返回的逆矩陣不為空
    /// 注:如果一個矩陣有逆矩陣,那矩陣和逆矩陣相乘結果等於單位矩陣
    /// </summary>
    /// <returns></returns>
    public Matrix GetInverseMatrix()
    {
        //step1:先篩選:某行或某列全為0的矩陣沒有逆矩陣(因為乘自己得到的結果是0矩陣)
        if (CheckZeroList() == false)
            return null;

        //幾個簡化原則:
        //1.單位矩陣的逆矩陣是本身
        if (GetUnitMatrixN(this.HangCount) == this)
            return this;
        ///其他性質
        ///2.A是奇異矩陣的話,他的逆矩陣的逆等於自己
        ///3.矩陣轉置的逆等於矩陣的逆的轉置
        ///4.(AB)-1 = (B-1)*(A-1)  -1表示-1次方,即矩陣乘積的逆等於反順序的矩陣逆的乘積

        //step2:先得到:代數餘子式的矩陣
        //GetConfactorMat();

        //step3:餘子式的矩陣要轉置:得到A的標準伴隨矩陣
        Matrix resMat = GetConfactorMat().GetTranspose();

        //step3:矩陣除以原矩陣的模
        float moderate = 1f / this.Mode;
        //Log.MyDebug("原矩陣的1/模=" + moderate);

        return resMat.MultiScalar(moderate);
    }

(7)判斷一個矩陣是否正交:

一個矩陣如果是正交的,那它的轉置矩陣和自身相乘的結果(矩陣為N行)應該等於N階單位矩陣。其中GetUnitMatrixN(int n)方法為獲取n階的單位矩陣:

  //檢查一個矩陣是否正交?
    public bool CheckOrthogonal()
    {
        Matrix lMat = this;
        if (lMat == NULL)
            return false;

        if (lMat.HangCount == 1 || lMat.LieCount == 1)
            return false;
        Matrix rMat = this.GetTranspose();
        if (rMat == NULL)
            return false;
        Matrix res = lMat * rMat;

        if (res == NULL)
            return false;

        Matrix resOther = GetUnitMatrixN(res.HangCount);
        return (res == resOther);
    }

最後附上工程的完整地址,我的github地址:

https://github.com/yuhezhangyanru/UnityStudy/tree/master/UnityStudy/Study1