首先奇异值分解(Singular Value Decomposition,以下简称SVD)描述如下:
奇异值分解(Singular Value Decomposition)是线性代数中一种重要的矩阵分解,奇异值分解则是特征分解在任意矩阵上的推广。在信号处理,统计学等领域有重要应用。
假设M是一个m×n阶矩阵,其中的元素全部属于域 K,也就是实数域或复数域。如此则存在一个分解使得
其中U是m×m阶矩阵;Σ是半正定m×n阶对角矩阵;而V*,即V的共轭转置,是n×n阶矩阵。这样的分解就称作M的奇异值分解。Σ对角线上的元素Σi,其中Σi即为M的奇异值。
常见的做法是为了奇异值由大而小排列。如此Σ便能由M唯一确定了。(虽然U和V仍然不能确定)
直观的解释
在矩阵M的奇异值分解中
·U的列(columns)组成一套对M的正交"输入"或"分析"的基向量。这些向量是MM*的特征向量。
·V的列(columns)组成一套对M的正交"输出"的基向量。这些向量是M*M的特征向量。
·Σ对角线上的元素是奇异值,可视为是在输入与输出间进行的标量的"膨胀控制"。这些是M*M及MM*的奇异值,并与U和V的列向量相对应。
我们用java实现如下:
首先我们需要用到特征分解,可以参考我之前写的一篇文章,里面使用了雅克比(Jacobi)算法进行特征分解
https://blog.csdn.net/luohualiushui1/article/details/88342768
然后我们实现SVD主方法如下:
public static DenseMatrix64F[] runSVD(DenseMatrix64F A) {
//求输入矩阵的转置
DenseMatrix64F A_T = new DenseMatrix64F(A.numCols,A.numRows);
CommonOps.transpose(A, A_T);
//求A.T*A
DenseMatrix64F ATA = new DenseMatrix64F(A.numCols,A.numCols);
CommonOps.mult(A_T,A,ATA);
//求A*A.T
DenseMatrix64F AAT = new DenseMatrix64F(A.numRows,A.numRows);
CommonOps.mult(A,A_T,AAT);
EDInfo ATA_de = Jacobi.JacobiCount(ATA,0.001,1000);
EDInfo AAT_de = Jacobi.JacobiCount(AAT,0.001,1000);
//计算奇异值
DenseMatrix64F singulars = new DenseMatrix64F(A.numRows,A.numCols);
singulars.zero();
for(int i=0;i<A.numCols;i++) {
singulars.set(i, i,Math.sqrt(ATA_de.getVectors()[i]));
}
return new DenseMatrix64F[]{AAT_de.getValues(),singulars,ATA_de.getValues()};
}
然后我们开始测试
double[][] datas = {{0,1},{1,1},{1,0}};
DenseMatrix64F[] usv = runSVD(new DenseMatrix64F(datas));
System.out.println(usv[0]);
System.out.println(usv[1]);
System.out.println(usv[2]);
测试结果如下:
这里我们用一个简单的例子来说明矩阵是如何进行奇异值分解的。我们的矩阵A定义为:
矩阵依次是U,Σ,V