背景
执行坐标变换的一工具就是使用矩阵乘法,这里给出矩阵乘法的简单实现。
矩阵乘法
核心代码
1 public Matrix Multiply(Matrix matrix) 2 { 3 if (this._n != matrix._m) 4 { 5 throw new InvalidOperationException("左侧矩阵的列不等于右侧矩阵的行"); 6 } 7 8 var resultMatrix = new Matrix(this._m, matrix._n); 9 10 for (var i = 0; i < resultMatrix._m; i++) 11 { 12 for (var j = 0; j < resultMatrix._n; j++) 13 { 14 resultMatrix._data[i][j] = Enumerable.Range(0, this._n).Sum(k => this._data[i][k] * matrix._data[k][j]); 15 } 16 } 17 18 return resultMatrix; 19 }
全部代码
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace DataStuctureStudy.Matrixes 8 { 9 class MatrixTest 10 { 11 public static void Test() 12 { 13 var matrixA = 14 new Matrix(3, 3) 15 .SetRowData(0, 1, 2, 3) 16 .SetRowData(1, 1, 2, 7) 17 .SetRowData(2, 4, 9, 2); 18 19 var matrixB = matrixA.Simplify(); 20 var matrixC = matrixA.Inverse(); 21 22 matrixA.Dispaly(); 23 matrixB.Dispaly(); 24 matrixC.Dispaly(); 25 matrixA.Multiply(matrixC).Dispaly(); 26 27 } 28 class Matrix 29 { 30 private readonly int _m; 31 private readonly int _n; 32 private readonly double[][] _data; 33 34 public Matrix(int m, int n) 35 { 36 _m = m; 37 _n = n; 38 _data = new double[m][]; 39 for (var i = 0; i < m; i++) 40 { 41 _data[i] = new double[n]; 42 } 43 } 44 45 public Matrix SetRowData(int row, params double[] values) 46 { 47 Array.Copy(values, _data[row], _n); 48 49 return this; 50 } 51 52 public Matrix SwapRow(int rowX, int rowY) 53 { 54 var resultMatrix = this.ConeSelf(); 55 56 var temp = resultMatrix._data[rowX]; 57 resultMatrix._data[rowX] = resultMatrix._data[rowY]; 58 resultMatrix._data[rowY] = temp; 59 60 return resultMatrix; 61 } 62 63 public Matrix MultiplyRow(int row, double operand) 64 { 65 var resultMatrix = this.ConeSelf(); 66 67 for (var i = 0; i < resultMatrix._n; i++) 68 { 69 resultMatrix._data[row][i] *= operand; 70 } 71 72 return resultMatrix; 73 } 74 75 76 public Matrix AddSourceRowToTargetRow(int sourceRow, double operand, int targetRow) 77 { 78 var resultMatrix = this.ConeSelf(); 79 80 for (var i = 0; i < resultMatrix._n; i++) 81 { 82 resultMatrix._data[targetRow][i] += resultMatrix._data[sourceRow][i] * operand; 83 } 84 85 return resultMatrix; 86 } 87 88 public Matrix Simplify() 89 { 90 var resultMatrix = this.ConeSelf(); 91 92 for (var col = 0; col < resultMatrix._n; col++) 93 { 94 if (col >= _m) 95 { 96 break; 97 } 98 99 if (resultMatrix._data[col][col] == 0) 100 { 101 var nonZeroRowIndex = resultMatrix.FindNonZeroRowIndex(col, col + 1); 102 if (nonZeroRowIndex == -1) 103 { 104 break; 105 } 106 resultMatrix = resultMatrix.SwapRow(col, nonZeroRowIndex); 107 } 108 109 resultMatrix = resultMatrix.MultiplyRow(col, 1 / resultMatrix._data[col][col]); 110 111 for (var row = 0; row < _m; row++) 112 { 113 if (row == col) 114 { 115 continue; 116 } 117 resultMatrix = resultMatrix.AddSourceRowToTargetRow(col, -1 * resultMatrix._data[row][col], row); 118 } 119 } 120 121 return resultMatrix; 122 } 123 124 public Matrix Multiply(Matrix matrix) 125 { 126 if (this._n != matrix._m) 127 { 128 throw new InvalidOperationException("左侧矩阵的列不等于右侧矩阵的行"); 129 } 130 131 var resultMatrix = new Matrix(this._m, matrix._n); 132 133 for (var i = 0; i < resultMatrix._m; i++) 134 { 135 for (var j = 0; j < resultMatrix._n; j++) 136 { 137 resultMatrix._data[i][j] = Enumerable.Range(0, this._n).Sum(k => this._data[i][k] * matrix._data[k][j]); 138 } 139 } 140 141 return resultMatrix; 142 } 143 144 public Matrix Inverse() 145 { 146 if (_m != _n) 147 { 148 throw new InvalidOperationException("只有方阵可以求逆"); 149 } 150 151 var combinedMatrix = this.Combine(this.E()); 152 combinedMatrix = combinedMatrix.Simplify(); 153 154 var resultMatrix = new Matrix(_m, _m); 155 for (var i = 0; i < resultMatrix._m; i++) 156 { 157 Array.Copy(combinedMatrix._data[i], resultMatrix._m, resultMatrix._data[i], 0, resultMatrix._m); 158 } 159 160 return resultMatrix; 161 } 162 163 public Matrix Combine(Matrix matrix) 164 { 165 if (this._m != matrix._m) 166 { 167 throw new InvalidOperationException("左侧矩阵的行不等于右侧矩阵的行"); 168 } 169 170 var resultMatrix = new Matrix(this._m, this._n + matrix._n); 171 for (var i = 0; i < resultMatrix._m; i++) 172 { 173 Array.Copy(this._data[i], 0, resultMatrix._data[i], 0, this._data[i].Length); 174 Array.Copy(matrix._data[i], 0, resultMatrix._data[i], this._data[i].Length, matrix._data[i].Length); 175 } 176 177 return resultMatrix; 178 } 179 180 public Matrix E() 181 { 182 return E(_m); 183 } 184 185 public static Matrix E(int m) 186 { 187 var matrix = new Matrix(m, m); 188 for (var i = 0; i < m; i++) 189 { 190 matrix._data[i][i] = 1; 191 } 192 193 return matrix; 194 } 195 196 public Matrix ConeSelf() 197 { 198 var resultMatrix = new Matrix(this._m, this._n); 199 for (var i = 0; i < this._m; i++) 200 { 201 Array.Copy(this._data[i], resultMatrix._data[i], this._n); 202 } 203 204 return resultMatrix; 205 } 206 207 public void Dispaly() 208 { 209 Console.WriteLine(); 210 foreach (var row in _data) 211 { 212 Console.WriteLine("[ " + String.Join(" , ", row) + " ]"); 213 } 214 Console.WriteLine(); 215 } 216 217 private int FindNonZeroRowIndex(int col, int startRow) 218 { 219 if (startRow >= _m) 220 { 221 return -1; 222 } 223 224 for (var i = startRow; i < _m; i++) 225 { 226 if (_data[i][col] != 0) 227 { 228 return i; 229 } 230 } 231 232 return -1; 233 } 234 } 235 } 236 }
备注
上例中的几个变换方法都实现为了无副作用的,面向数学概念的结构最好实现为这样的,因为数学中的方法就是无副作用的。