Детерминант матрицы на C#
Детерминант (или определитель) матрицы можно вычислить огромным количеством способов. Мы воспользуемся следующей формулой:
где — определитель матрицы, полученной из исходной вырезанием 1 строки и j столбца.
Для начала научимся вырезать из матрицы столбец:
private Matrix CreateMatrixWithoutColumn(int column) { if (column < 0 || column >= this.N) { throw new ArgumentException("invalid column index"); } var result = new Matrix(this.M, this.N - 1); result.ProcessFunctionOverData((i, j) => result[i, j] = j < column ? this[i, j] : this[i, j + 1]); return result; }
Здесь все просто: сдвигаем все элементы на 1 влево, если они находятся справа от вырезаемого столбца.
Аналогично вырезаем строку
private Matrix CreateMatrixWithoutRow(int row) { if (row < 0 || row >= this.M) { throw new ArgumentException("invalid row index"); } var result = new Matrix(this.M - 1, this.N); result.ProcessFunctionOverData((i, j) => result[i, j] = i < row ? this[i, j] : this[i + 1, j]); return result; }
Добавим в класс матрицы свойство — квадратная она или нет
public bool IsSquare { get => this.M == this.N; }
Теперь можно считать детерминант матрицы
public double CalculateDeterminant() { if (!this.IsSquare) { throw new InvalidOperationException( "determinant can be calculated only for square matrix"); } if (this.N == 2) { return this[0, 0] * this[1, 1] - this[0, 1] * this[1, 0]; } double result = 0; for (var j = 0; j < this.N; j++) { result += (j % 2 == 1 ? 1 : -1) * this[1, j] * this.CreateMatrixWithoutColumn(j). CreateMatrixWithoutRow(1).CalculateDeterminant(); } return result; }
Разберем по частям. Первая — проверка на квадратность. Она нужна потому, что посчитать определитель можно только у квадратно матрицы.
Следующая часть — определитель матрицы размера 2. Этот кусок кода нужен для выхода из рекурсии (а наша функция рекурсивна).
if (this.N == 2) { return this[0, 0] * this[1, 1] - this[0, 1] * this[1, 0]; }
и потом идет уже сама формула детерминанта. Непонятным может быть кусок
j % 2 == 1 ? 1 : -1
это просто способ посчитать -1 в степени j + 1.
Кеширование результата
Вычисление определителя — достаточно тяжелая операция. Логично будет добавить кеширование результата. Добавим приватное поле
private double precalculatedDeterminant = double.NaN;
в начале вычисления детерминанта вернем закешированное значение, если оно есть
if (!double.IsNaN(this.precalculatedDeterminant)) { return this.precalculatedDeterminant; }
если его нет — его нужно сохранить после вычисления. Самое важное при работе с кешированием — инвалидация кеша. В нашем случае сбрасывать кеш нужно тогда, когда изменится значение какого-либо элемента матрицы. Для этого доработаем свойство-индексатор
public double this[int x, int y] { get { return this.data[x, y]; } set { this.data[x, y] = value; this.precalculatedDeterminant = double.NaN; } }
Рабочий код как всегда лежит на гитхабе