二維矩陣運算實作練習,包括基本加減法、純量乘法、矩陣乘法、行列式、伴隨矩陣、轉置矩陣、反矩陣
翻一下之前自己寫的一些程式範例,發現這個二維矩陣運算功能,紀錄一下免得忘記了,本範例最終魔王是反矩陣啊!!囧rz
雖然實務上使用到二維運算往往是用到三方套件,比如:OpenCV;但作為練習熟悉C#的基本操作來說還蠻適合的實作範例
尤其是數位影像處理、機器學習、AI運算等等演算法的實作,大都是架設在矩陣運算的基礎上,順便也重新熟悉當初線代學的一些知識點
首先架構基礎屬性跟建構子,這個類別我是命名為Matrix2D,不例外就先設定行、列的索引變數跟宣告double的二維陣列資料,特別是重載this[,]這個索引子運算子方便後面運算
public int Row { get; set; }
public int Column { get; set; }
public double[,] Data { get; set; }
public double this[int row, int col]
{
get { return Data[row, col]; }
set { Data[row, col] = value; }
}
public Matrix2D(int row, int col)
{
Row = row;
Column = col;
Data = new double[row, col];
for (var i = 0; i < row; i++)
{
for (var j = 0; j < col; j++)
{
Data[i, j] = 0;
}
}
}
再來就重載一下加(+)減(-)乘(*)運算子,可以簡潔程式碼(我怕看到眼花了!XD),同時也實作純量乘法
矩陣乘法(AxB)部分要注意,被乘數矩陣(A)的行數(column)要與乘數矩陣(B)的列數(row)要相同才能運算!!否則就返回null吧~
(另外提一點:大陸慣用的行列跟臺灣慣用的行列是相反的,如果查閱對岸文章時要注意這點!最好是用row與column表示就比較清楚)
public static Matrix2D operator -(Matrix2D Left, Matrix2D Right)
{
var minus = new Matrix2D(Left.Row, Left.Column);
for (var i = 0; i < minus.Row; i++)
{
for (var j = 0; j < minus.Column; j++)
{
minus[i, j] = Left[i, j] - Right[i, j];
}
}
return minus;
}
public static Matrix2D operator +(Matrix2D Left, Matrix2D Right)
{
var sum = new Matrix2D(Left.Row, Left.Column);
for (var i = 0; i < sum.Row; i++)
{
for (var j = 0; j < sum.Column; j++)
{
sum[i, j] = Left[i, j] + Right[i, j];
}
}
return sum;
}
public static Matrix2D operator *(Matrix2D M, double Scalar)
{
var RM = new Matrix2D(M.Row, M.Column);
for (var i = 0; i < RM.Row; i++)
{
for (var j = 0; j < RM.Column; j++)
{
RM[i, j] = M[i, j] * Scalar;
}
}
return RM;
}
public static Matrix2D operator *(double Scalar, Matrix2D M)
{
var RM = new Matrix2D(M.Row, M.Column);
for (var i = 0; i < RM.Row; i++)
{
for (var j = 0; j < RM.Column; j++)
{
RM[i, j] = M[i, j] * Scalar;
}
}
return RM;
}
public static Matrix2D operator *(Matrix2D Left, Matrix2D Right)
{
if (Left.Column != Right.Row)
{
return null;
}
var product = new Matrix2D(Left.Row, Right.Column);
for (var i = 0; i < product.Row; i++)
{
for (var j = 0; j < product.Column; j++)
{
product[i,j] = 0;
for (int k = 0; k < Left.Column; k++)
{
product[i, j] += Left[i, k] * Right[k, j];
}
}
}
return product;
}
完成好以上運算後,這邊實作比較簡單的轉置矩陣(Transpose Matrix)
public static Matrix2D Transpose(Matrix2D M)
{
var RM = new Matrix2D(M.Column, M.Row);
for (var i = 0; i < RM.Row; i++)
{
for (var j = 0; j < RM.Column; j++)
{
RM[i, j] = M[j, i];
}
}
return RM;
}
這邊的Minor函式是指餘子矩陣,正式的餘子式(Minor)還要將餘子矩陣作行列式
為什麼這樣取名呢?因為我在後方行列式會用道遞迴運算,這邊指是當前導功能來運用!
(總不會希望做出來的行列式要設定邊界,然後只有算3x3或2x2這樣吧! 囧~)
public static Matrix2D Minor(Matrix2D M, int i, int j)
{
var RM = new Matrix2D(M.Row - 1, M.Column - 1);
for (var a = 0; a < RM.Row; a++)
{
for (var b = 0; b < RM.Column; b++)
{
var p = (a >= i) ? (a + 1) : a;
var q = (b >= j) ? (b + 1) : b;
RM[a, b] = M[p, q];
}
}
return RM;
}
第一個重頭戲行列式(Determinant),參照Wiki的行列式說明
public static double? Determinant(Matrix2D M)
{
if ((M.Row <= 1) || (M.Column <= 1))
{
return null;
}
if (M.Row != M.Column)
{
return null;
}
double? result = 0;
if (M.Row == 2)
{
return ((M[0, 0]*M[1, 1]) - (M[0, 1]*M[1, 0]));
}
for (var i = 0; i < M.Column; i++)
{
result += M[0, i]*(Math.Pow(-1, (i+2)) * Determinant( Minor(M, 0, i) ));
}
return result;
}
過來再實作計算反矩陣需要的伴隨矩陣(Adjugate Matrix),一樣參照Wiki的伴隨矩陣說明
public static Matrix2D Adjugate(Matrix2D M)
{
var RM = new Matrix2D(M.Column, M.Row);
for (var i = 0; i < RM.Row; i++)
{
for (var j = 0; j < RM.Column; j++)
{
RM[i, j] = (double)(Math.Pow(-1, (i + j + 2)) * Determinant(Minor(M, j, i)));
}
}
return RM;
}
最終面對本次的重重重….點!反矩陣(Inverse Matrix),照舊參照Wiki的反矩陣說明~~(沒辦法,他最有權威姓!…!...偷懶中 XD)
public static Matrix2D Inverse(Matrix2D M)
{
var RM = new Matrix2D(M.Column, M.Row);
var detM = Determinant(M);
if (detM == null)
{
return null;
}
if (detM == 0)
{
return null;
}
RM = Adjugate(M) * (1 / (double)detM);
return RM;
}
最後的反矩陣怎麼看來這麼簡單!?那是前置工作做掉了~囧
這個範例重點還是矩陣運算的規則,C#一些小技巧~結束,灑花~XD