Java 为什么使用部分旋转的行缩减比不使用旋转的行缩减更糟糕?

Java 为什么使用部分旋转的行缩减比不使用旋转的行缩减更糟糕?,java,matrix,linear-algebra,numerical-analysis,numerical-stability,Java,Matrix,Linear Algebra,Numerical Analysis,Numerical Stability,先说声对不起。这将是一个漫长的过程 一些背景。我一直在用Java实现几个与矩阵相关的算法。我正在编制缩减行梯队表(rref)。我已经实现了使用和不使用部分旋转的rref。我的理解是,部分旋转应该比不旋转在数值上更稳定。然而,我看到了一些矩阵的相反情况,我的实现导致我认为我做错了什么 关于我的实现的一些注意事项: 我已经实现了自己的Number类。此类允许使用复数或实数 类中的compareTo()方法使用复数的大小。通过另一种方法对实际值进行比较 我的Matrix类基本上包装了这些Number

先说声对不起。这将是一个漫长的过程

一些背景。我一直在用Java实现几个与矩阵相关的算法。我正在编制缩减行梯队表(rref)。我已经实现了使用和不使用部分旋转的rref。我的理解是,部分旋转应该比不旋转在数值上更稳定。然而,我看到了一些矩阵的相反情况,我的实现导致我认为我做错了什么

关于我的实现的一些注意事项:

  • 我已经实现了自己的
    Number
    类。此类允许使用复数或实数
  • 类中的
    compareTo()
    方法使用复数的大小。通过另一种方法对实际值进行比较
  • 我的
    Matrix
    类基本上包装了这些
    Number
    对象的2d数组,并提供了几个构造函数/方法
代码:
无部分旋转:

public static Matrix rrefNoPivot(Matrix A) {
    Number m, scale;
    
    int pivotRow = 0,
        pivotCol = 0;
    
    while(pivotRow<A.m && pivotCol<A.n) {
        if(!A.entries[pivotRow][pivotCol].equals(Number.ZERO)) { 
            scale = Number.divide(Number.ONE, A.entries[pivotRow][pivotCol]);
             
            for(int k=pivotCol; k<A.n; k++) { // scale the whole row
                A.entries[pivotRow][k] = Number.multiply(A.entries[pivotRow][k], scale); 
            }   
        }
            
        for(int i=0; i<A.m; i++) {
            m = A.entries[i][pivotCol];
            
            if(pivotRow != i) {
                for(int k=pivotCol; k<A.n; k++) {
                    A.entries[i][k] = Number.subtract(A.entries[i][k], 
                            Number.multiply(A.entries[pivotRow][k], m));
                }
            }
        }
        
        pivotRow++;
        pivotCol++;
    }
    
    return A;
}
因此,有非常小的非零值(应该是零)导致了问题。这似乎是浮点算术错误的结果。C是相似的。然而,对于矩阵B,部分旋转确实产生了正确的答案,而非旋转方法有一些错误

下面是我的正式问题,最后请回答。首先,是否存在我实施部分旋转的问题?无论是数值不稳定性的引入还是不正确的逻辑。第二,是否保证部分旋转在数值上更稳定?或者,当使用部分旋转不太稳定时,这两个矩阵是E和C的例子吗?这些类型的错误是不可避免的吗?我是否应该将非常小的数字舍入为零,这样算法才能工作?
先谢谢你们

public static Matrix rref(Matrix A) {
    Number mult, scale, currentMax;
    int maxIndex;
    
    int pivotRow = 0,
        pivotCol = 0;
    
    while(pivotRow<A.m && pivotCol<A.n) {   
        maxIndex = pivotRow;
        currentMax = A.entries[pivotRow][pivotCol];
        
        for(int i=pivotRow; i<A.m; i++) { // find the maximum entry in the pivot column (at or below the pivot ).
            if(A.entries[i][pivotCol].compareTo(currentMax) > 0) {
                maxIndex = i;
                currentMax = A.entries[i][pivotCol];
            }
        }
        
        if(!A.entries[maxIndex][pivotCol].equals(Number.ZERO)) { // Check that the maximum absolute value is not zero.
            if(pivotRow != maxIndex) {
                A = A.swapRows(pivotRow, maxIndex); // Make the row with the largest value in the pivot column the pivot for this row.
            }
            
            scale = Number.divide(Number.ONE, A.entries[pivotRow][pivotCol]);
             
            for(int k=pivotCol; k<A.n; k++) { // scale the whole row
                A.entries[pivotRow][k] = Number.multiply(A.entries[pivotRow][k], scale); 
            }
            
            for(int i=0; i<A.m; i++) {
                mult = A.entries[i][pivotCol];
                
                if(pivotRow != i) {
                    for(int k=pivotCol; k<A.n; k++) {
                        A.entries[i][k] = Number.subtract(A.entries[i][k], 
                                Number.multiply(A.entries[pivotRow][k], mult));
                    }
                }
            }
            
            pivotRow++;
            pivotCol++;
        }
        else { // Then we do not have a pivot for this column (i.e. the column is all zeros).
            pivotCol++;
        }
    }
    
    return A;
}
E: rref(E): rrefNoPivot(E): [ [1 2 3 ] [ [1 0 0] [ [1 0 -1] [4 5 6 ] [0 1 0] [0 1 2 ] [7 8 9 ] [0 0 1] [0 0 0 ] [10 11 12] [0 0 0] [0 0 0 ] [13 14 15] ] [0 0 0] ] [0 0 0 ] ] C: rref(C): rrefNoPivot(C): [ [1 2 3 4 5 ] [ [1 0 0 -0.9333333333333338 -1] [ [1 0 -1 -2 -3] [6 7 8 9 10] [0 1 0 0.8666666666666671 0 ] [0 1 2 3 4 ] [11 12 13 14 15] ] [0 0 1 1.0666666666666667 2 ] ] [0 0 0 0 0 ] ] B: rref(B): rrefNoPivot(B): [ [1 2 3 1 2 3 4 5 6] [ [1 0 0 0 0 0 0 0 0] [ [1 0 0 0 0 0 0 2.220446049250313E-16 0] [4 5 6 3 4 5 7 8 2] [0 1 0 0 0 0 0 0 0] [0 1 0 0 0 0 0 -4.440892098500626E-16 0] [1 5 5 2 6 7 9 0 1] [0 0 1 0 0 0 0 0 0] [0 0 1 0 0 0 0 4.440892098500626E-16 0] [3 4 5 2 6 7 8 9 2] [0 0 0 1 0 0 0 0 0] [0 0 0 1 0 0 0 -1.3877787807814457E-17 0] [1 1 1 3 4 7 8 9 1] [0 0 0 0 1 0 0 0 0] [0 0 0 0 1 0 0 -4.440892098500626E-16 0] [3 4 7 8 3 1 2 3 4] [0 0 0 0 0 1 0 0 0] [0 0 0 0 0 1 0 8.881784197001252E-16 0] [3 5 6 8 1 3 5 9 1] [0 0 0 0 0 0 1 0 0] [0 0 0 0 0 0 1 -4.440892098500626E-16 0] [0 4 5 3 2 0 1 2 3] [0 0 0 0 0 0 0 1 0] [0 0 0 0 0 0 0 0.9999999999999999 0] [1 4 5 1 7 0 5 3 1] ] [0 0 0 0 0 0 0 0 1] ] [0 0 0 0 0 0 0 7.105427357601002E-15 1] ] [ [1 0 -1.0000000000000002 ] [0 1 2 ] [0 0 1.7763568394002505E-15] [0 0 0 ] [0 0 8.881784197001252E-16 ] ]