【C#】C# で 行列計算を自作する ~ 逆行列編 ~

■ はじめに

https://blogs.yahoo.co.jp/dk521123/38068315.html
で数値演算ライブラリを扱ったが、
今回は、行列計算を自作で実装してみる

逆行列の求め方

 * 逆行列の求め方は以下の通り。

  [1] 掃き出し法 (Row reduction)・ガウスジョルダンの消去法 (Gauss–Jordan elimination)
 [2] ガウスの消去法 (Gaussian elimination)
  [3] LU分解 (LU Factorization)

etc...
http://mugichoko.hatenablog.com/entry/2018/04/05/184100

[1] 掃き出し法

 * とりあえず「単位行列E」の形にすることを目標(=連立方程式を解くことになる)に
   対角成分を1に、それ以外の成分を0にする
https://linky-juku.com/row-reduction/
https://www.youtube.com/watch?v=Da73Ra7gWKU

[2] ガウスの消去法

 * 行列表示された連立方程式の係数行列を上三角行列に変形(前進消去)し
   今度は逆方向に解を求めていく(後退代入)という手法

  + 前進消去 (Forward Elimination)   : 文字を1つずつ消していく操作
  + 後退代入 (Backward Substitution) : 1成分ずつ答えを求めていく操作
https://mathtrain.jp/gausssyokyo
http://imagingsolution.blog.fc2.com/blog-entry-106.html

[3] LU分解

 * 行列を下三角行列L(Lower triangle matrix)と上三角行列U(Upper triangle matrix)の積LUに分解すること

 【下三角行列 L】 【上三角行列 U】
  | □ □ □ |     | □ ■ ■ |
  | ■ □ □ |     | □ □ ■ |
  | ■ ■ □ |     | □ □ □ |

 Ax = b (A : 行列、b : 行列)
  => LUx = b
     で、Aを「L」と「U」という行列に分解する


■ サンプル

例1:掃き出し法

using System;
using System.Windows.Forms;

namespace SampleForm
{
  public partial class Form1 : Form
  {
    public Form1()
    {
      InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
      var inputs= new double[][]{
        new double[] {1,2,0,-1},
        new double[] {-1,1,2,0},
        new double[] {2,0,1,1},
        new double[] {1,-2,-1,1}
      };
      CalculateInverseMatrix(inputs, out double[][] outputs);

      this.label1.Text = string.Empty;
      foreach (var rows in outputs)
      {
        foreach (var value in rows)
        {
          this.label1.Text += value + ", ";
        }
        this.label1.Text += "\n";
      }
    }

    private static bool CalculateInverseMatrix(
      double[][] targetMatrixes, out double[][] outInverseMatrixes)
    {
      var dataNumber = targetMatrixes.GetLength(0);
      outInverseMatrixes = new double[dataNumber][];
      //単位行列を作る
      for (int i = 0; i < dataNumber; i++)
      {
        outInverseMatrixes[i] = new double[dataNumber];
        for (int j = 0; j < dataNumber; j++)
        {
          outInverseMatrixes[i][j] = (i == j) ? 1.0f : 0.0f;
        }
      }

      // 掃き出し法
      for (int i = 0; i < dataNumber; i++)
      {
        if (targetMatrixes[i][i] == 0)
        {
          return false;
        }
        var tempValue = 1.0 / targetMatrixes[i][i];
        for (int j = 0; j < dataNumber; j++)
        {
          targetMatrixes[i][j] *= tempValue;
          outInverseMatrixes[i][j] *= tempValue;
        }
        for (int j = 0; j < dataNumber; j++)
        {
          if (i != j)
          {
            tempValue = targetMatrixes[j][i];
            for (int k = 0; k < dataNumber; k++)
            {
              targetMatrixes[j][k] -= targetMatrixes[i][k] * tempValue;
              outInverseMatrixes[j][k] -= outInverseMatrixes[i][k] * tempValue;
            }
          }
        }
      }
      return true;
    }
  }
}
出力結果
2, 2, -1, 3, 
-4, -5, 3, -7, 
3, 4, -2, 5, 
-7, -8, 5, -11,

■ 補足:double型 ⇒ float型に変えた場合

 * 以下のように変換すると、誤差が出てダメだった。
   (以下の「出力結果(float型)」参照)
変換前
private static bool CalculateInverseMatrix(
  double[][] targetMatrixes, out double[][] outInverseMatrixes)

etc...
変換後
private static bool CalculateInverseMatrix(
  float[][] targetMatrixes, out float[][] outInverseMatrixes)

etc...

出力結果(float型)

誤差が出て使えない...
1.999998, 1.999998, -0.9999989, 2.999997, 
-3.999996, -4.999995, 2.999997, -6.999992, 
2.999997, 3.999997, -1.999998, 4.999995, 
-6.999993, -7.999992, 4.999995, -10.99999,


関連記事

数値演算ライブラリ Math.Net Numerics

https://blogs.yahoo.co.jp/dk521123/38068315.html

C#】画像処理 ~ アフィン変換 ~

https://blogs.yahoo.co.jp/dk521123/38061211.html