矩陣基礎演算法實現
最近花了一段時間去溫習矩陣的基礎演算法,要去敲程式碼的時候才發現原來以前的線性程式碼全都白學了,沒有去用沒有去實現的結果就等於什麼都沒用。
自己為了練習實現了一些矩陣的基礎演算法,算是一個溫習,一條條羅列下來。其中我的矩陣資料為浮點數格式,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