Работа с матрицами в C#

17.12.2018 at 10:37

Матрица представляет из себя двумерный массив. Для начала создадим класс матрицы

public class Matrix {
    private double[,] data;

    private int m;
    public int M {get => this.m;}
    
    private int n;
    public int N {get => this.n;}
    
    public Matrix(int m, int n) 
    {
        this.m = m;
        this.n = n;
        this.data = new double[m, n];
    }
}

Пока этот класс явлеятся просто оберткой для двумерного массива.

Начинаем добавлять вспомогательные методы. Сначала добавим метод, позволяющий выполнить какое-либо действие над всеми элементами матрицы.

public void ProcessFunctionOverData(Action<int, int> func)
{
    for(var i = 0; i < this.M; i++)
    {
        for(var j = 0; j < this.N; j++)
        {
            func(i, j);
        }
    }
}

Функции высших порядков в C#

Функции высших порядков — это функции, которые как переменные передаются в аргументы других функций. В методе выше аргумент func имеет тип Action. Это значит, что аргументом должна быть функция, принимающая 2 аргумента типа int и возвращающая void. Добавим в конструктор такой вызов:

this.ProcessFunctionOverData((i, j) => this.data[i, j] = 0);

Этот вызов присвоит 0 во все элементы матрицы. Стрелочная функция (i, j) => this.data[i, j] = 0 — это и есть наша функция высшего порядка.

Использовать стрелочные функции не обязательно, можно было передавать и метод:

private void SetToZero(int i, int j)
{
    this.data[i, j] = 0;
}
...
this.ProcessFunctionOverData(this.SetToZero);

Индексаторы в C#

Индексаторы — это свойства, позволяющие обращаться к объекту как к массиву. Добавим такое свойство, позволяющее обращаться к элементам матрицы по индексу

public double this[int x, int y] 
{
    get 
    {
        return this.data[x, y];
    }
    set {
        this.data[x, y] = value;
    }        
}

Теперь к экземпляру класса Matrix можно обращаться как к двумерному массиву.

Умножение матриц на C#

Сначала реализуем умножение матрицы на число. В результате должна получиться матрица того же размера, элементами будут элементы исходной матрицы умноженные на это число.

Перегрузка операторов в C#

Для классов в C# можно определить операторы. Определим оператор умножения, позволяющиу умножить на число.

public static Matrix operator* (Matrix matrix, double value) 
{
    var result = new Matrix(matrix.M, matrix.N);
    result.ProcessFunctionOverData((i, j) => 
        result[i,j] = matrix[i,j] * value);
    return result;
}

Теперь умножить матрицу на число можно с помощью оператора умножения

var testMatrix = new Matrix(2, 3);
var multipliedMatrix = testMatrix * 2.1;

Матрицу можно так же умножить на другую матрицу. Результатом будет новая матрица, элементами которой будут скалярные произведения столбцов и строк исходных матриц. Формулу можно посмотреть на википедии

Перегрузка методов в C#

C# позволяет определить несколько методов с одинаковым названием. Воспользуемся этип и сделаем еще одну реализацию оператора умножения, позволяющую умножать матрицу на другую матрицу

public static Matrix operator* (Matrix matrix, Matrix matrix2) 
{
    if (matrix.N != matrix2.M) {
        throw new ArgumentException("matrixes can not be multiplied");
    }
    var result = new Matrix(matrix.M, matrix2.N);
    result.ProcessFunctionOverData((i, j) => {
        for(var k = 0; k < matrix.N; k++) {
            result[i, j] += matrix[i, k] * matrix2[k, j];
        }
    });
    return result;
}

Весь исходный код можно найти на гитхабе

Tags: