[C#] 基礎二維矩陣運算(2D Matrix)實作

二維矩陣運算實作練習,包括基本加減法、純量乘法、矩陣乘法、行列式、伴隨矩陣、轉置矩陣、反矩陣

翻一下之前自己寫的一些程式範例,發現這個二維矩陣運算功能,紀錄一下免得忘記了,本範例最終魔王是反矩陣啊!!囧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