为什么使用部分透视执行的行减少比不使用透视更糟糕?

问题描述

提前抱歉。这将是一个漫长的过程。

一些背景。我一直在用 Java 实现几个与矩阵相关的算法。我正在研究减少的行梯队形式(rref)。我已经在使用和不使用部分旋转的情况下实现了 rref。我的理解是部分旋转应该比不旋转在数值上更稳定。但是,我发现某些矩阵与我的实现相反,这让我认为我做错了什么。

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

  • 我已经实现了自己的 Number 类。此类允许使用复数或实数。
  • compareto() 类中的 Number 方法使用复数的大小。实际值的比较是通过另一种方法完成的。
  • 我的 Matrix 类本质上包装了这些 Number 对象的二维数组,并提供了多个构造函数/方法

代码
没有部分旋转:

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;
}

部分旋转:

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,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],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] ]

对于前两个,rref() 甚至没有得到正确答案。我查看了矩阵,在 while 循环的倒数第二次迭代期间,这就是它的样子......


    [ [1  0  -1.0000000000000002   ]
      [0  1  2                     ]
      [0  0  1.7763568394002505E-15]
      [0  0  0                     ]
      [0  0  8.881784197001252E-16 ] ]

所以有非常小的非零值(应该是零)导致了这个问题。这似乎是浮点算术错误的结果。 C 类似。然而,对于矩阵 B,部分旋转确实产生了正确的答案,而无旋转方法有一些错误

所以这是我的正式问题,最后是sheesh。首先,我实施部分旋转有问题吗?无论是引入数值不稳定还是不正确的逻辑。其次,部分旋转是否保证在数值上更稳定?或者,使用部分旋转时的两个矩阵 E 和 C 示例是否不太稳定?这些类型的错误是不可避免的吗?我应该将非常小的数字四舍五入为零以便算法起作用吗?
提前谢谢大家。

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)