负面情况下几乎正确的矩阵分解代码失败

时间:2013-07-30 15:20:02

标签: c# math matrix decomposition

我正在编写自己的矩阵类,其中包含矩阵类所需的方法。

代码的内容取自here

除了我的Decompose方法之外,一切似乎都很完美(有关LU分解矩阵的含义的更多信息,请查看here):

    public Matrix Decompose(out int[] permutationArray, out int toggle)
    {
        // Doolittle LUP decomposition with partial pivoting.
        // rerturns: result is L (with 1s on diagonal) and U; perm holds row permutations; toggle is +1 or -1 (even or odd)

        if (!this.IsSquare())
        {
            throw new Exception("LU-Decomposition can only be found for a square matrix");
        }

        Matrix decomposedMatrix = new Matrix(this); // copy this matrix before messing with it

        permutationArray = new int[NumberOfRows]; // set up row permutation result
        for (int i = 0; i < NumberOfRows; ++i)
        {
            permutationArray[i] = i;
        }

        toggle = 1; // toggle tracks row swaps. +1 -> even, -1 -> odd. used by MatrixDeterminant

        for (int columnIndex = 0; columnIndex < NumberOfRows - 1; columnIndex++) // each column
        {
            // find largest value in col j
            double maxOfColumn = Math.Abs(decomposedMatrix.GetElement(columnIndex, columnIndex));

            int pivotRowIndex = columnIndex;

            for (int rowIndex = columnIndex + 1; rowIndex < NumberOfRows; rowIndex++)
            {
                if (Math.Abs(decomposedMatrix.GetElement(rowIndex,columnIndex)) > maxOfColumn)
                {
                    maxOfColumn = decomposedMatrix.GetElement(rowIndex, columnIndex);
                    pivotRowIndex = rowIndex;
                }
            }

            if (pivotRowIndex != columnIndex) // if largest value not on pivot, swap rows
            {
                double[] rowPtr = decomposedMatrix.GetRow(pivotRowIndex);
                decomposedMatrix.SetRowOfMatrix(pivotRowIndex, decomposedMatrix.GetRow(columnIndex));
                decomposedMatrix.SetRowOfMatrix(columnIndex, rowPtr);

                int tmp = permutationArray[pivotRowIndex]; // and swap permutation info
                permutationArray[pivotRowIndex] = permutationArray[columnIndex];
                permutationArray[columnIndex] = tmp;

                toggle = -toggle; // adjust the row-swap toggle
            }

            if (decomposedMatrix.GetElement(columnIndex, columnIndex) == 0.0)
            {
                // find a good row to swap
                int goodRow = -1;
                for (int row = columnIndex + 1; row < decomposedMatrix.NumberOfRows; row++)
                {
                    if (decomposedMatrix.GetElement(row, columnIndex) != 0.0)
                    {
                        goodRow = row;
                    }
                }

                if (goodRow == -1)
                {
                    throw new Exception("Cannot use Doolittle's method on this matrix");
                }

                // swap rows so 0.0 no longer on diagonal
                double[] rowPtr = decomposedMatrix.GetRow(goodRow);
                decomposedMatrix.SetRowOfMatrix(goodRow, decomposedMatrix.GetRow(columnIndex));
                decomposedMatrix.SetRowOfMatrix(columnIndex, rowPtr);

                int tmp = permutationArray[goodRow]; // and swap perm info
                permutationArray[goodRow] = permutationArray[columnIndex];
                permutationArray[columnIndex] = tmp;

                toggle = -toggle; // adjust the row-swap toggle

            }

            for (int rowIndex = columnIndex + 1; rowIndex < this.NumberOfRows; ++rowIndex)
            {
                double valueToStore = decomposedMatrix.GetElement(rowIndex, columnIndex) / decomposedMatrix.GetElement(columnIndex, columnIndex);
                decomposedMatrix.SetElement(rowIndex, columnIndex, valueToStore);
                for (int nextColumnIndex = columnIndex + 1; nextColumnIndex < NumberOfRows; ++nextColumnIndex)
                {
                    double valueToStore2 = decomposedMatrix.GetElement(rowIndex, nextColumnIndex) -
                                            decomposedMatrix.GetElement(rowIndex, columnIndex) * decomposedMatrix.GetElement(columnIndex, nextColumnIndex);
                    decomposedMatrix.SetElement(rowIndex, nextColumnIndex, valueToStore2);
                }
            }

        } // main j column loop

        return decomposedMatrix;
    } // MatrixDecompose

这是失败的单元测试:(请注意!如果开始矩阵使用正数而不是负数,则完全相同的单位测试通过)

    [TestMethod()]
    public void Matrix_DecomposeTest3_DifferentSigns()
    {
        Matrix matrixA = new Matrix(9);

        //Set up matrix A
        double[] row1OfMatrixA = { 3, 0, 0, -7, 0, 0, 0, 0, 0 };
        double[] row2OfMatrixA = { 0, 1, 8, 0, -4, 2, 0, 0, 0 };
        double[] row3OfMatrixA = { 0, 2, 8, 0, -9, 3, 0, 0, 0 };
        double[] row4OfMatrixA = { -5, 0, 0, 4, 0, 7, -1, 0, 3 };
        double[] row5OfMatrixA = { 0, -3, -7, 0, 7, -8, 0, -3, 0 };
        double[] row6OfMatrixA = { 0, 7, 2, 5, -3, 7, -2, 0, 4 };
        double[] row7OfMatrixA = { 0, 0, 0, -2, 0, -8, 4, 0, 7 };
        double[] row8OfMatrixA = { 0, 0, 0, 0, -9, 0, 0, 3, 0 };
        double[] row9OfMatrixA = { 0, 0, 0, 1, 0, 4, 7, 0, 6 };

        matrixA.SetRowOfMatrix(0, row1OfMatrixA);
        matrixA.SetRowOfMatrix(1, row2OfMatrixA);
        matrixA.SetRowOfMatrix(2, row3OfMatrixA);
        matrixA.SetRowOfMatrix(3, row4OfMatrixA);
        matrixA.SetRowOfMatrix(4, row5OfMatrixA);
        matrixA.SetRowOfMatrix(5, row6OfMatrixA);
        matrixA.SetRowOfMatrix(6, row7OfMatrixA);
        matrixA.SetRowOfMatrix(7, row8OfMatrixA);
        matrixA.SetRowOfMatrix(8, row9OfMatrixA);

        Console.Out.Write(matrixA);

        //The LUP Decomposition

        //Correct L Part
        Matrix correctLPartOfLUPDecomposition = new Matrix(9);

        double[] row1OfLMatrix = { 1, 0, 0, 0, 0, 0, 0, 0, 0 };
        double[] row2OfLMatrix = { 0, 1, 0, 0, 0, 0, 0, 0, 0 };
        double[] row3OfLMatrix = { 0, .143, 1, 0, 0, 0, 0, 0, 0 };
        double[] row4OfLMatrix = { -.6, 0, 0, 1, 0, 0, 0, 0, 0 };
        double[] row5OfLMatrix = { 0, 0, 0, 0, 1, 0, 0, 0, 0 };
        double[] row6OfLMatrix = { 0, 0, 0, .435, 0, 1, 0, 0, 0 };
        double[] row7OfLMatrix = { 0, 0, 0, -.217, 0, -.5, 1, 0, 0 };
        double[] row8OfLMatrix = { 0, -.429, -.796, -.342, -.319, .282, -.226, 1, 0 };
        double[] row9OfLMatrix = { 0.000, 0.286, 0.963, 0.161, 0.523, 0.065, 0.013, 0.767, 1.000 };

        correctLPartOfLUPDecomposition.SetRowOfMatrix(0, row1OfLMatrix);
        correctLPartOfLUPDecomposition.SetRowOfMatrix(1, row2OfLMatrix);
        correctLPartOfLUPDecomposition.SetRowOfMatrix(2, row3OfLMatrix);
        correctLPartOfLUPDecomposition.SetRowOfMatrix(3, row4OfLMatrix);
        correctLPartOfLUPDecomposition.SetRowOfMatrix(4, row5OfLMatrix);
        correctLPartOfLUPDecomposition.SetRowOfMatrix(5, row6OfLMatrix);
        correctLPartOfLUPDecomposition.SetRowOfMatrix(6, row7OfLMatrix);
        correctLPartOfLUPDecomposition.SetRowOfMatrix(7, row8OfLMatrix);
        correctLPartOfLUPDecomposition.SetRowOfMatrix(8, row9OfLMatrix);

        //Correct U Part
        Matrix correctUPartOfLUPDecomposition = new Matrix(9);

        double[] row1OfUMatrix = { -5.000, 0.000, 0.000, 4.000, 0.000, 7.000, -1.000, 0.000, 3.000 };
        double[] row2OfUMatrix = { 0.000, 7.000, 2.000, 5.000, -3.000, 7.000, -2.000, 0.000, 4.000 };
        double[] row3OfUMatrix = { 0.000, 0.000, 7.714, -0.714, -3.571, 1.000, 0.286, 0.000, -0.571 };
        double[] row4OfUMatrix = { 0.000, 0.000, 0.000, -4.600, 0.000, 4.200, -0.600, 0.000, 1.800 };
        double[] row5OfUMatrix = { 0.000, 0.000, 0.000, 0.000, -9.000, 0.000, 0.000, 3.000, 0.000 };
        double[] row6OfUMatrix = { 0.000, 0.000, 0.000, 0.000, 0.000, -9.826, 4.261, 0.000, 6.217 };
        double[] row7OfUMatrix = { 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 9.000, 0.000, 9.500 };
        double[] row8OfUMatrix = { 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, -2.043, 2.272 };
        double[] row9OfUMatrix = { 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, -3.153 };

        correctUPartOfLUPDecomposition.SetRowOfMatrix(0, row1OfUMatrix);
        correctUPartOfLUPDecomposition.SetRowOfMatrix(1, row2OfUMatrix);
        correctUPartOfLUPDecomposition.SetRowOfMatrix(2, row3OfUMatrix);
        correctUPartOfLUPDecomposition.SetRowOfMatrix(3, row4OfUMatrix);
        correctUPartOfLUPDecomposition.SetRowOfMatrix(4, row5OfUMatrix);
        correctUPartOfLUPDecomposition.SetRowOfMatrix(5, row6OfUMatrix);
        correctUPartOfLUPDecomposition.SetRowOfMatrix(6, row7OfUMatrix);
        correctUPartOfLUPDecomposition.SetRowOfMatrix(7, row8OfUMatrix);
        correctUPartOfLUPDecomposition.SetRowOfMatrix(8, row9OfUMatrix);

        //Calculate values for the above
        int[] permutationArray;
        int toggleValue;
        Matrix decomposeResult = matrixA.Decompose(out permutationArray, out toggleValue);

        Matrix calculatedLPartOfLUPDecomposition = decomposeResult.ExtractLower();
        Matrix calculatedUPartOfLUPDecomposition = decomposeResult.ExtractUpper();

        //Compare the two matrices
        double tolerance = .001;
        Matrix LDifferenceMatrix = calculatedLPartOfLUPDecomposition - correctLPartOfLUPDecomposition;
        Matrix UDifferenceMatrix = calculatedUPartOfLUPDecomposition - correctUPartOfLUPDecomposition;

        for (int i = 0; i < LDifferenceMatrix.NumberOfRows; i++)
        {
            for (int j = 0; j < LDifferenceMatrix.NumberOfColumns; j++)
            {
                (Math.Abs(LDifferenceMatrix.GetElement(i, j)) < tolerance).Should().BeTrue();
            }
        }

        for (int i = 0; i < UDifferenceMatrix.NumberOfRows; i++)
        {
            for (int j = 0; j < UDifferenceMatrix.NumberOfColumns; j++)
            {
                (Math.Abs(UDifferenceMatrix.GetElement(i, j)) < tolerance).Should().BeTrue();
            }
        }
    }

我使用找到的here工具获得了单元测试的数字。

我有单元测试了Matrix的所有其他方法,它们都通过了。

我对分解方法有什么看法?

2 个答案:

答案 0 :(得分:1)

在哪里选择结果列的最大值:

maxOfColumn = decomposedMatrix.GetElement(rowIndex, columnIndex);

你应该真正选择那里找到的最大绝对值(你在if语句中做到这一点,之后似乎已经忘记了)。我会通过添加评论来明确指出,以便下一个阅读此代码的人可以更清楚地看到它

 //if a value with a larger absolute value has been found replace the currently selectedmax
 if (Math.Abs(decomposedMatrix.GetElement(rowIndex,columnIndex)) > maxOfColumn)
 {
    maxOfColumn = Math.Abs(decomposedMatrix.GetElement(rowIndex, columnIndex));

答案 1 :(得分:0)

if (Math.Abs(decomposedMatrix.GetElement(rowIndex,columnIndex)) > maxOfColumn)
{
    maxOfColumn = decomposedMatrix.GetElement(rowIndex, columnIndex);

应该是:

if (Math.Abs(decomposedMatrix.GetElement(rowIndex,columnIndex)) > maxOfColumn)
{
    maxOfColumn = Math.Abs(decomposedMatrix.GetElement(rowIndex, columnIndex));