在C#

时间:2017-10-10 01:58:34

标签: c# matrix

根据此处给出的算法https://en.wikipedia.org/wiki/Gaussian_elimination#Pseudocode
我试图实现高斯消除的C#版本:

public Matrix GaussianElimination(double epsilon = 1e-10)
{
    var result = Copy();

    var kMax = Math.Min(result.RowCount, result.ColumnCount);

    for (var k = 0; k < kMax; k++)
    {
        // Find k-th pivot, i.e. maximum in column max
        var iMax = result.FindColumnAbsMax(k);

        if (Math.Abs(result[iMax, k]) < epsilon)
        {
            throw new ArithmeticException("Matrix is singular or nearly singular.");
        }

        // Swap maximum row with current row
        SwapRows(k, iMax);

        // Make all rows below the current one, with 0 in current column
        for (var i = k + 1; i < result.RowCount; i++)
        {
            var factor = result[i, k] / result[k, k];

            for (var j = k + 1; j < result.ColumnCount; j++)
            {
                result[i, j] = result[i, j] - result[k, j] * factor;
            }

            result[i, k] = 0;
        }
    }

    return result;
}

在大多数情况下都适用(请注意,这不执行后退替换步骤)。但是,对于维基百科中给出的第一个示例,算法停止在奇异矩阵的情况下,并在返回替换步骤之前在行梯形表上抛出相关的异常:

public class Program
{
    public static void Main(string[] args)
    {
        var matrix = Matrix.Parse("[1 3 1 9; 1 1 -1 1; 3 11 5 35]");

        Console.WriteLine(matrix);

        Console.WriteLine(matrix.GaussianElimination());

        Console.ReadKey();
    }
}
  • k = 2
  • kMax = 3
  • iMax = 2
  • result = [1 3 1 9; 0 -2 -2 -8; 0 0 0 0]

[编辑] Matrix实现有点冗长,但这里是字符串解析和消除方法的用法:

public class Matrix
{
    public const string MatrixStart = "[";
    public const string MatrixStop = "]";
    public const char RowSeparator = ';';
    public const char RowCellSeparator = ' ';

    public int RowCount { get; }

    public int ColumnCount { get; }

    public int CellCount => _data.Length;

    public bool IsSquare => RowCount == ColumnCount;

    public bool IsVector => ColumnCount == 1;

    private readonly double[] _data;

    public double this[int rowIndex, int columnIndex]
    {
        get => _data[Convert2DIndexTo1DIndex(rowIndex, columnIndex)];
        set => _data[Convert2DIndexTo1DIndex(rowIndex, columnIndex)] = value;
    }

    public Matrix(Matrix matrix)
        : this(matrix.RowCount, matrix.ColumnCount)
    {
        for (var i = 0; i < _data.Length; i++)
        {
            _data[i] = matrix._data[i];
        }
    }

    public Matrix(int size, double placeHolder = 0)
        : this(size, size, placeHolder)
    {
    }

    public Matrix(int rowCount, int columnCount, double initializationValue = 0)
    {
        if (rowCount < 0)
        {
            throw new ArgumentOutOfRangeException(nameof(rowCount));
        }
        if (columnCount < 0)
        {
            throw new ArgumentOutOfRangeException(nameof(columnCount));
        }

        RowCount = rowCount;
        ColumnCount = columnCount;
        _data = new double[RowCount * ColumnCount];

        if (Math.Abs(initializationValue) > 0)
        {
            Set(initializationValue);
        }
    }

    public Matrix(int rowCount, int columnCount, double[] initializationValues)
    {
        if (rowCount < 0)
        {
            throw new ArgumentOutOfRangeException(nameof(rowCount));
        }
        if (columnCount < 0)
        {
            throw new ArgumentOutOfRangeException(nameof(columnCount));
        }
        if (initializationValues.Length != rowCount * columnCount)
        {
            throw new ArgumentOutOfRangeException(nameof(initializationValues));
        }

        RowCount = rowCount;
        ColumnCount = columnCount;
        _data = new double[initializationValues.Length];
        initializationValues.CopyTo(_data, 0);
    }

    private int Convert2DIndexTo1DIndex(int rowIndex, int columnIndex)
    {
        return rowIndex * ColumnCount + columnIndex;
    }

    public void Set(double value)
    {
        for (var i = 0; i < _data.Length; i++)
        {
            _data[i] = value;
        }
    }

    public Matrix Transpose()
    {
        var result = new Matrix(ColumnCount, RowCount);

        for (var i = 0; i < result.RowCount; i++)
        {
            for (var j = 0; j < result.ColumnCount; j++)
            {
                result[i, j] = this[j, i];
            }
        }

        return result;
    }

    public Matrix Copy()
    {
        return new Matrix(this);
    }

    private int FindColumnAbsMax(int index)
    {
        return FindColumnAbsMax(index, index);
    }

    private int FindColumnAbsMax(int rowStartIndex, int columnIndex)
    {
        var maxIndex = rowStartIndex;

        for (var i = rowStartIndex + 1; i < RowCount; i++)
        {
            if (Math.Abs(this[maxIndex, columnIndex]) <= Math.Abs(this[i, columnIndex]))
            {
                maxIndex = i;
            }
        }

        return maxIndex;
    }


    public void SwapRows(int rowIndexA, int rowIndexB)
    {
        for (var j = 0; j < ColumnCount; j++)
        {
            var indexA = Convert2DIndexTo1DIndex(rowIndexA, j);
            var indexB = Convert2DIndexTo1DIndex(rowIndexB, j);

            _data.Swap(indexA, indexB);
        }
    }

    public void SwapColumns(int columnIndexA, int columnIndexB)
    {
        for (var i = 0; i < RowCount; i++)
        {
            var indexA = Convert2DIndexTo1DIndex(i, columnIndexA);
            var indexB = Convert2DIndexTo1DIndex(i, columnIndexB);

            _data.Swap(indexA, indexB);
        }
    }

    public static Matrix Parse(string matrixString)
    {
        if (!matrixString.StartsWith(MatrixStart) || !matrixString.EndsWith(MatrixStop))
        {
            throw new FormatException();
        }

        matrixString = matrixString.Remove(0, 1);
        matrixString = matrixString.Remove(matrixString.Length - 1, 1);

        var rows = matrixString.Split(new[] { RowSeparator }, StringSplitOptions.RemoveEmptyEntries);

        if (rows.Length <= 0)
        {
            return new Matrix(0, 0);
        }

        var cells = ParseRow(rows[0]);
        var matrix = new Matrix(rows.Length, cells.Length);

        for (var j = 0; j < cells.Length; j++)
        {
            matrix[0, j] = cells[j];
        }

        for (var i = 1; i < matrix.RowCount; i++)
        {
            cells = ParseRow(rows[i]);

            for (var j = 0; j < cells.Length; j++)
            {
                matrix[i, j] = cells[j];
            }
        }

        return matrix;
    }

    private static double[] ParseRow(string row)
    {
        var cells = row.Split(new [] { RowCellSeparator }, StringSplitOptions.RemoveEmptyEntries);
        return cells.Select(x => Convert.ToDouble(x.Replace(" ", string.Empty))).ToArray();
    }
}

用于交换两个项目的1D Array扩展:

public static class ArrayHelpers
{
    public static void Swap<TSource>(this TSource[] source, int indexA, int indexB)
    {
        var tmp = source[indexA];
        source[indexA] = source[indexB];
        source[indexB] = tmp;
    }
}

0 个答案:

没有答案