/* ************************ * * * 行列関係のライブラリ * * * ************************ * * ・行列の要素 A.Value[m, n] * ・n次単位行列(static) Matrix.Identity(n) * ・m×n型零行列(static) Matrix.Zero(m, n) * ・行列式(static) Matrix.Determinant(A) * ・逆行列 A.Inverse() * ・m行n列目を除いた小行列 A.SubMatrix(m, n) ただし、行、列の開始は0 * ・転置行列 A.Transpose() * * 以下は演算子のオーバーロード * ・型が同じで同じ要素を持つ行列の比較 == * ・型が同じで同じ要素を持つ行列の比較 != * ・行列の足し算 A + B * ・行列の引き算 A - B * ・行列の掛け算 A * B * ・スカラー倍 2 * A * ・スカラー倍 A * 2 * * クラス作成の方法
* ・行列の型を指定し、後から値を代入 var A = new Matrix(2, 3);
* A.Value = new decimal[2, 3] { { 1, 2, 3 }, { 4, 5, 6 } };
* ・値を代入しながら行列を作成 var A = new Matrix(new decimal[3, 3] { { 1, 0, 1 }, { 0, 1, 0 }, { 0, 0, 1 } }); * * 2014.2.17完成 * */ using System; using System.Text; namespace MatrixLibrary {
public class Matrix {
public decimal[,] Value;
private int rowLength, columnLength;
public Matrix(int m, int n) // 行列の型だけを指定するconstructor {
// m <= 0またはn <= 0の場合はエラーを返す if (m <= 0 || n <= 0)
rowLength = m; columnLength = n;
Value = new decimal[m, n]; // m×n型行列 }
public Matrix(decimal[,] elementValue) // 値を代入しながら行列を生成するconstructor {
rowLength = elementValue.GetLength(0); columnLength = elementValue.GetLength(1); Value = new decimal[rowLength, columnLength]; for (var r = 0; r < rowLength; r++)
for (var c = 0; c < columnLength; c++) Value[r, c] = elementValue[r, c]; }
// Override ToString()
public override string ToString() {
var val = new StringBuilder(); val.Clear();
for (var r = 0; r < rowLength; r++) {
for (var c = 0; c < columnLength; c++)
val.Append(Value[r, c].ToString() + " "); val.Append("\n"); } return val.ToString(); } // identity matrix
public static Matrix Identity(int n) // n次の単位行列を返す {
// n <=0 の場合はエラーを返す if (n <= 0)
throw new ArgumentException("単位行列の生成:作成される行列は行数、列数ともに正の整数でなくてはなりません"); var I = new Matrix(n, n);
for (var r = 0; r < n; r++) for (var c = 0; c < n; c++) I.Value[r, c] = 0; for (var r = 0; r < n; r++) I.Value[r, r] = 1; return I; } // zero matrix
{
// m <= 0またはn <= 0の場合はエラーを返す if (m <= 0 || n <= 0)
throw new ArgumentException("零行列の生成:作成される行列は行数、列数ともに正の整数でなくてはなりません"); var Z = new Matrix(m, n);
for (var r = 0; r < m; r++) for (var c = 0; c < n; c++) Z.Value[r, c] = 0; return Z; } // determinant
public static decimal Determinant(Matrix X) // 行列を第1列について展開し、行列式の値を求める {
// 正方行列でないとエラーを返す if (X.rowLength != X.columnLength)
throw new ArgumentException("行列式:正方行列でない行列では行列式を計算できません"); return determinantSub(X);
}
private static decimal determinantSub(Matrix X) {
var tmpMat = new Matrix(X.rowLength, X.columnLength); for (var r = 0; r < X.rowLength; r++)
for (var c = 0; c < X.columnLength; c++) tmpMat.Value[r, c] = X.Value[r, c];
if (tmpMat.rowLength == 1) // 1×1行列ならその値をそのまま返す return tmpMat.Value[0, 0];
else if (tmpMat.rowLength == 2) // 2×2行列になったら行列式の値を返す
return tmpMat.Value[0, 0] * tmpMat.Value[1, 1] - tmpMat.Value[0, 1] * tmpMat.Value[1, 0]; else if (tmpMat.rowLength == 3) // 3×3行列になったら行列式の値を返す
{
decimal det = 0;
det += tmpMat.Value[0, 0] * tmpMat.Value[1, 1] * tmpMat.Value[2, 2]; det += tmpMat.Value[0, 1] * tmpMat.Value[1, 2] * tmpMat.Value[2, 0]; det += tmpMat.Value[0, 2] * tmpMat.Value[1, 0] * tmpMat.Value[2, 1]; det -= tmpMat.Value[0, 2] * tmpMat.Value[1, 1] * tmpMat.Value[2, 0]; det -= tmpMat.Value[0, 1] * tmpMat.Value[1, 0] * tmpMat.Value[2, 2]; det -= tmpMat.Value[0, 0] * tmpMat.Value[1, 2] * tmpMat.Value[2, 1]; return det;
}
else // 第1列について展開 {
decimal det = 0;
if (tmpMat.Value[r, 0] != 0)
det += tmpMat.Value[r, 0] * determinantSub(tmpMat.SubMatrix(r, 0)) * (r % 2 == 0 ? 1 : (-1)); return det;
} }
// inverse
public Matrix Inverse() {
// 正方行列でないとエラーを返す if (rowLength != columnLength)
throw new ArgumentException("逆行列:正方行列でない行列では逆行列を計算できません"); if (Determinant(this) == 0)
throw new ArgumentException("逆行列:行列式の値が0の行列は逆行列を計算できません"); var result = new Matrix(rowLength, columnLength);
if (rowLength == 1)
result.Value[0, 0] = 1 / Value[0, 0]; else if (rowLength == 2)
{
decimal det = Determinant(this);
result.Value[0, 0] = Value[1, 1] / det; result.Value[0, 1] = -Value[0, 1] / det; result.Value[1, 0] = -Value[1, 0] / det; result.Value[1, 1] = Value[0, 0] / det; }
else {
decimal det = Determinant(this); for (var r = 0; r < rowLength; r++)
for (var c = 0; c < columnLength; c++)
result.Value[r, c] = cofactor(r, c) / det; }
return result; }
private decimal cofactor(int r, int c) // 余因子行列の行列式を返す {
return Determinant(SubMatrix(c, r) * ((c + r) % 2 == 0 ? 1 : (-1))); }
// row行目とcol列目を除いた小行列 ただし、最初の行は0行目、最初の列は0列目 public Matrix SubMatrix(int row, int col)
// rowが行列の行数を上回ったり、colが行列の列数を上回るとエラーを返す if (row >= rowLength || col >= columnLength)
throw new ArgumentException("小行列の作成:除くべき行、列番号がもとの行列の行数または列数を上回っています"); // row<0またはcol<0の場合はエラーを返す
if (row < 0 || col < 0)
throw new ArgumentException("小行列の作成:除くべき行、列番号は0以上である必要があります"); var result = new Matrix(rowLength - 1, columnLength - 1);
var rr = 0;
for (var r = 0; r < rowLength; r++) {
if (r != row) {
var cc = 0;
for (var c = 0; c < columnLength; c++) if (c != col) result.Value[rr, cc++] = Value[r, c]; rr++; } } return result; } // Transpose
public Matrix Transpose() {
var result = new Matrix(columnLength, rowLength); for (var r = 0; r < rowLength; r++)
for (var c = 0; c < columnLength; c++) result.Value[c, r] = Value[r, c]; return result;
}
// Overloading '==' operator
public static Boolean operator ==(Matrix A, Matrix B) // 行列の型が同じですべての要素の値がそれぞれ等しい時にtrue {
if (A.rowLength != B.rowLength) return false; if (A.columnLength != B.columnLength) return false; for (var r = 0; r < A.rowLength; r++)
for (var c = 0; c < A.columnLength; c++)
if (A.Value[r, c] != B.Value[r, c]) return false; return true;
// Overloading '!=' operator
public static Boolean operator !=(Matrix A, Matrix B) // 行列の型が異なったり、要素の値が1つでも異なるとfalse {
if (A.rowLength != B.rowLength) return true; if (A.columnLength != B.columnLength) return true; for (var r = 0; r < A.rowLength; r++)
for (var c = 0; c < A.columnLength; c++)
if (A.Value[r, c] != B.Value[r, c]) return true; return false;
}
// Overloading '+' operator
public static Matrix operator +(Matrix A, Matrix B) {
// AとBの型が異なるとエラーを返す
if (A.rowLength != B.rowLength || A.columnLength != B.columnLength)
throw new ArgumentException("行列の和:左の行列と右の行列の型が一致しません"); var result = new Matrix(A.rowLength, A.columnLength);
for (var r = 0; r < A.rowLength; r++)
for (var c = 0; c < A.columnLength; c++)
result.Value[r, c] = A.Value[r, c] + B.Value[r, c]; return result;
}
// Overloading '-' operator
public static Matrix operator -(Matrix A, Matrix B) {
// AとBの型が異なるとエラーを返す
if (A.rowLength != B.rowLength || A.columnLength != B.columnLength)
throw new ArgumentException("行列の差:左の行列と右の行列の型が一致しません"); var result = new Matrix(A.rowLength, A.columnLength);
for (var r = 0; r < A.rowLength; r++)
for (var c = 0; c < A.columnLength; c++)
result.Value[r, c] = A.Value[r, c] - B.Value[r, c]; return result;
}
// Overloading '*' operator
public static Matrix operator *(Matrix A, Matrix B) // 行列どうしの積 {
// Aの列数とBの行数が一致しないときにエラーを返す if (A.columnLength != B.rowLength)
var result = new Matrix(A.rowLength, B.columnLength); for (var r = 0; r < A.rowLength; r++)
for (var c = 0; c < B.columnLength; c++) {
result.Value[r, c] = 0;
for (var i = 0; i < A.columnLength; i++)
result.Value[r, c] += A.Value[r, i] * B.Value[i, c]; }
return result; }
// Overloading '*' operator
public static Matrix operator *(decimal d, Matrix A) // 行列のスカラー倍(スカラーは左から) {
var result = new Matrix(A.rowLength, A.columnLength); for (var r = 0; r < A.rowLength; r++)
for (var c = 0; c < A.columnLength; c++) result.Value[r, c] = d * A.Value[r, c]; return result;
}
// Overloading '*' operator
public static Matrix operator *(Matrix A, decimal d) // 行列のスカラー倍(スカラーは右から) {
var result = new Matrix(A.rowLength, A.columnLength); for (var r = 0; r < A.rowLength; r++)
for (var c = 0; c < A.columnLength; c++) result.Value[r, c] = d * A.Value[r, c]; return result;
} } }