Java JAMA矩阵问题

4

我正在使用jama来计算SVD。如果我传递正方形矩阵,例如2x2或3x3等,它的工作非常好。但是当我传递像这样的2x3或4x8这样的矩阵时,它会给出错误。我使用了他们所有的示例。他们有不同的构造函数来执行此任务。另外我的第二个问题是,我使用了3x3矩阵,但它给出了

double[][] vals = {{1.,1.,0},{1.,0.,1.},{1.,3.,4.},{6.,4.,8.}};
  Matrix A = new Matrix(vals);

它产生了以下错误:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 3

之后,我考虑使用另一个构造函数,如下所示:
double[][] vals = {{1.,1.,0,4},{1.,0.,1.,2},{1.,3.,4.,8},{1.,3.,4.,8}};
  Matrix A = new Matrix(vals,4,3);

它产生了以下输出:
A = 
 1.0 1.0 0.0
 1.0 0.0 1.0
 1.0 3.0 4.0
 6.0 4.0 8.0

A = U S V^T

U = 
 0.078 -0.115 -0.963
 0.107 -0.281 0.260
 0.402 0.886 -0.018
 0.906 -0.351 0.060

Sigma = 
 11.861881 0.000000 0.000000
 0.000000 2.028349 0.000000
 0.000000 0.000000 1.087006

V = 
 0.507705 -0.795196 -0.331510
 0.413798 0.562579 -0.715735
 0.755650 0.226204 0.614675

rank = 3
condition number = 10.912437186202627
2-norm = 11.86188091889931
singular values = 
 11.861881 2.028349 1.087006

它适用于非方阵。但是对于svd来说,它会产生错误的结果,因为V和S的行数不相同(如果我不能正确分析结果,我很抱歉,因为我是svd的新手)。有什么想法吗?应该怎么办?

4个回答

5

注意,JAMA主要支持完全秩矩阵的奇异值分解(SVD),如果您阅读“readme”文件,您会注意到对于秩缺陷(m < n)的矩阵,其行为不一定正确。

实质上,导致您的ArrayIndexOutOfBounds异常的是SingularValueDecomposition中的第486行:

return new Matrix(U,m,Math.min(m+1,n));

将此更改为:
return new Matrix(U);

将解决问题。在vicatcu的示例中,最终发生的事情是您注入了一个矩阵,其中m = 4n = 5,但请注意实际输出中U的尺寸为m = 4n = 4。如果您阅读SingularValueDecomposition类的顶部,它指出:

对于一个m x n矩阵A(其中m≥n),奇异值分解是一个m x n正交矩阵U,一个n x n对角线矩阵S和一个n x n正交矩阵V,使得A = USV'。

但是,在这种情况下,这不成立,因为m = 4n = 5意味着m<n。因此,现在由于您正在传递一个秩缺失矩阵,所以U的尺寸与SVD类的正常调用案例不同,因此有如下声明:
new Matrix(U, m, Math.min(m+1,n))

这段代码将创建一个矩阵,假设行数为m,这里是4(正确的),列数为n,这里是Math.min(4+1,5)=5(不正确)。所以:当您打印矩阵并且打印程序调用getColumnDimension时,U矩阵返回5,这大于实际支持数组维度。

简而言之,切换到我上面粘贴的代码行将检测U的维度,并因此返回有效结果,而不管秩如何。


感谢您提供的解决方案。我想使用Jama运行多元回归,并遇到了同样的问题。也许我的问题很奇怪,但我无法编辑SingularValueDecomposition类。这意味着NetBeans不允许我编辑Jama代码。 - MTT

1

请阅读奇异值分解维基百科文章。下面的代码是第二部分示例的代表。

import Jama.Matrix; 
import Jama.SingularValueDecomposition; 

public class JAMATest { 

    static public void printMatrix(Matrix m){
        double[][] d = m.getArray();

        for(int row = 0; row < d.length; row++){
            for(int col = 0; col < d[row].length; col++){
                System.out.printf("%6.4f\t", m.get(row, col));
            }
            System.out.println();
        }
        System.out.println();
    }

    public static void main(String[] args) { 
        double[][] vals = { {1., 0., 0., 0., 2.}, 
                            {0., 0., 3., 0., 0.}, 
                            {0., 0., 0., 0., 0.}, 
                            {0., 4., 0., 0., 0.} 
                          };  
        Matrix A = new Matrix(vals);         
        SingularValueDecomposition svd = new SingularValueDecomposition(A); 

        System.out.println("A = ");
        printMatrix(A);

        System.out.println("U = ");
        printMatrix(svd.getU());

        System.out.println("Sigma = ");
        printMatrix(svd.getS());

        System.out.println("V = ");
        printMatrix(svd.getV());
    } 
} 

并产生输出:

A = 
1.0000  0.0000  0.0000  0.0000  2.0000  
0.0000  0.0000  3.0000  0.0000  0.0000  
0.0000  0.0000  0.0000  0.0000  0.0000  
0.0000  4.0000  0.0000  0.0000  0.0000  

U = 
0.0000  0.0000  -1.0000 0.0000  
0.0000  1.0000  -0.0000 0.0000  
0.0000  0.0000  -0.0000 1.0000  
1.0000  0.0000  -0.0000 0.0000  

Sigma = 
4.0000  0.0000  0.0000  0.0000  0.0000  
0.0000  3.0000  0.0000  0.0000  0.0000  
0.0000  0.0000  2.2361  0.0000  0.0000  
0.0000  0.0000  0.0000  0.0000  0.0000  
0.0000  0.0000  0.0000  0.0000  0.0000  

V = 
0.0000  -0.0000 -0.4472 -0.8944 -0.0000 
0.0000  -0.0000 -0.0000 -0.0000 -0.0000 
0.0000  1.0000  -0.0000 -0.0000 -0.0000 
0.0000  -0.0000 -0.0000 -0.0000 1.0000  
1.0000  -0.0000 -0.8944 0.4472  -0.0000 

希望这可以帮到你。另外,就此问题,以下是Matlab的输出结果:
>> A = [1.0000,  0.0000,  0.0000,  0.0000,  2.0000; 0, 0, 3, 0, 0; 0, 0, 0, 0, 0; 0, 4, 0, 0, 0];
>> A

A =

     1     0     0     0     2
     0     0     3     0     0
     0     0     0     0     0
     0     4     0     0     0

>> [U, S, V] = svd(A);
>> U

U =

     0     0     1     0
     0     1     0     0
     0     0     0    -1
     1     0     0     0

>> S

S =

    4.0000         0         0         0         0
         0    3.0000         0         0         0
         0         0    2.2361         0         0
         0         0         0         0         0

>> V

V =

         0         0    0.4472         0   -0.8944
    1.0000         0         0         0         0
         0    1.0000         0         0         0
         0         0         0    1.0000         0
         0         0    0.8944         0    0.4472

关于您的第一个问题,以下代码不会产生错误:
import Jama.Matrix;

public class JAMATest {

    /**
     * @param args
     */
    public static void main(String[] args) {
        double[][] vals = {{1.,1.,0},{1.,0.,1.},{1.,3.,4.},{6.,4.,8.}}; 
        Matrix A = new Matrix(vals); 

    }
}

所以你正在做的其他事情一定会导致它出现异常。尝试使用我的printMatrix方法替换你正在使用的任何东西,看看是否有帮助。


很抱歉,我无法理解你的回答。你认为我在问题中写的输出是否正确?3行有多少个V和S?能否请你解释一下?很抱歉,我不是数学专家:( - user238384
对我之前的程序进行了修改,我包含了我的printMatrix函数。请注意,当我尝试使用m.getRowDimension()和m.getColumnDimension()时,我遇到了与您相似的异常,因为它们似乎对svd.getU()方法不准确。将矩阵转换为double[][]似乎可以解决这个问题。 - vicatcu
另外需要注意的是,矩阵的奇异值分解并不唯一。参考维基百科文章中的这句话:“还应该注意到,这种特殊的奇异值分解并不唯一。” 当你将 U * S * V 相乘时,会得到原始矩阵。当你进行矩阵乘法时,矩阵的维度会“合并”。因此,我们的原始矩阵为 [4 x 5] = [4 x 4] * [4 x 5] * [5 x 5],最终只剩下了外部两个维度([4 ..x.. 5])。根据这些属性,似乎你必须认识到 Sigma 的前四行才是实际的矩阵。 - vicatcu
@agazerboy,从Matlab和Java的输出结果可以看出,Matlab输出中U的第3列和第4列与Java输出中的符号不同--这是可以接受的,因为U形成了一个正交归一基,因此任何一列都可以被取反,这是可以接受的。 - Mark Elliot
你好..我在奇异值分解的单数S、左奇异向量U和右奇异向量V中得到了所有的NAN值。矩阵太大了(146,534378)。有什么解决方法吗? - Gaurav Koradiya

0

U、S和V的维度不需要与A的维度相同。U将具有相同数量的行,而V^T将具有相同数量的列。这足以通过矩阵乘法的规则重新创建A。

另一个维度(U的列、V^T的行和S的行/列)将是A的“秩”(在您的示例中为3)。粗略地说,这是您的数据的维数...需要多少轴才能唯一表示A中的列或行。它最多将是min(rows, cols),但通常可以小得多。这没问题。


0
Jama不支持完整的SVD,只支持简化的SVD。它相当于Matlab中的svd(B,0)或svd(B,'econ')。再见。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接