lanczos算法及C++实现(二)实对称阵奇异值分解的QR算法

1. 基本的QR算法

我们先讨论一般对阵矩阵的QR算法,再讨论对称三对角阵的QR算法

给定一个实对称阵X,假设其特征值分解为X=PSP',其中P对正交阵,S是对角阵。求P,S的QR算法如下,其中 $Q_k$为正交阵,$R_k$为上三角阵:


$X_1=X$
for k=1,2, ...
  $X_k=Q_k*R_k$ (QR分解)
  $X_{k+1}=R_k*Q_k$
endfor
$P=Q_1*Q_2* \dots *Q_{\infty}$
$R_{\infty}$的非对角线元素置0即得S

 

用matlab代码表示,即

function [P,S]=qreigen
A=rand(10);
A=A+A'      %Symmetry real matrix, otherwise P'AP is a block upper triangle matrix
m=size(A,1);
P=eye(m);
for i=1:1000
        [Q,R]=qr(A); %QR factorization
        A=R*Q;
        P=P*Q;
end
S=diag(diag(R));
end

 

这个算法很简单,每次迭代做一个QR分解和一个矩阵乘法。最基本的,我们用施密特正交化进行QR分解,这在上一篇lanczos算法及C++实现(一)框架及简单实现中使用。A讨论了QR方法和幂迭代方法的关系。

值得注意的是,如果矩阵X不是对称阵,这个方法往往不能收敛到一个上三角阵,而是一个块状上三角阵

function [P,A]=qreigen
A=rand(10); %not symmetry
m=size(A,1);
P=eye(m);
for i=1:1000
        [Q,R]=qr(A); %QR factorization
        A=R*Q;
        P=P*Q;
end
% A is block upper triangle matrix, not upper triangle
A
end

 

 运行结果如下:

A =
4.7120   -0.6211    0.4352   -0.0450   -0.1297    0.5700   -0.3769    0.4790   -0.0754    0.2798
     0    0.0273    0.8587   -0.2188   -0.0095   -0.1913   -0.3785   -0.2142   -0.0565   -0.1236
     0   -0.9150    0.3761   -0.0914    0.0217   -0.1527    0.0317    0.4235    0.4612   -0.3284
     0   -0.0000    0.0000   -0.7400   -0.0686    0.2954    0.1561   -0.1755    0.1879   -0.2237
     0   -0.0000    0.0000   -0.0000    0.4096   -0.5238   -0.0330    0.1636    0.3090   -0.4849
     0    0.0000    0.0000   -0.0000    0.2002    0.7429    0.2064   -0.4820   -0.2602   -0.2005
     0         0         0    0.0000   -0.0000   -0.0000   -0.3812   -0.1792   -0.0188   -0.4469
     0         0         0    0.0000   -0.0000    0.0000    0.4117   -0.1513    0.1134   -0.2247
     0         0         0    0.0000   -0.0000    0.0000    0.0000    0.0000   -0.3465    0.0887
     0         0         0         0         0         0   -0.0000   -0.0000   -0.0000    0.1833

 

2. 快速QR算法

施密特正交法每次需要O(n^3)的计算,很慢。所以通常不用这个方法。一般来说,我们采用如下QR算法,该算法分成两步:

1、利用Householder变换 (如果你对Householder变换不了解,请见附录B),将X正交变换为上海森伯格阵(Upper Hessenburg Matrix),复杂度O(n^3)。

2、在每次迭代中,利用Givens变换 (如果你对Givens变换不了解,请见附录C),求得上海森伯格阵的QR分解,复杂度O(n^2)。

这样只有在迭代之前需要O(n^3)的预处理,而每次迭代只要平方复杂度,因此比施密特正交法快很多。

2.1 矩阵的上海森伯格化 Hessenburg Reduction

在这一步,对一个方阵X(不要求对称),我们要求一个正交变换P,使得PXP'为上海森伯格矩阵,即低于对角线下一行的所有元素为0:
$PXP^T=\left(\begin{matrix}* & * & *& *& \dots \\ * & * & *& *& \dots \\ & * & *& *& \dots \\ & & *& *& \dots \\ & & & *& \dots \\ & & & & \dots \end{matrix}\right)$

如何求得这样的P呢?基本思想如下,将矩阵X分成4块:

$\left(\begin{matrix}x_{11} & X_{1,2:n}\\ X_{2:n,1} & X_{2:n,2:n} \end{matrix}\right)$

然后用Householder变换,将左下的一列变为只有一个非零元素:

$P_1 X_{2:n,1} = v_1 \mathbf e_1=\left(\begin{matrix}v_1\\ 0 \\ 0 \\ \dots \end{matrix}\right)$
其中$P_1=I-2u*u^T$是一个正交矩阵,

$u=\frac{X_{2:n,1}-\|X_{2:n,1}\|\mathbf e_1}{\left\|X_{2:n,1}-\|X_{2:n,1}\|\mathbf e_1\right\|}$

$ \mathbf e_i$是一个除第i个分量为1以外,其他分量为0的向量。P1的具体求法,参见附录B

这样我们有

$\left(\begin{matrix}1 & 0 \\ 0 & P_1 \end{matrix}\right) X \left(\begin{matrix}1 & 0 \\ 0 & P^T_1 \end{matrix}\right) = \left(\begin{matrix}x_{11} & X^*_{1,2:n} \\ v_1\mathbf e_1 & X^*_{2:n,2:n} \end{matrix}\right) $

类似的,我们可以构造$\left(\begin{matrix}1 & 0 & 0 \\ 0 & 1 & 0 \\ 0 & 0 & P_2\end{matrix}\right)$等共n-1个矩阵,使得X正交相似于一个上海森伯格矩阵。

值得一提的是,如果X是一个对称阵,那么根据对称性,X正交相似于一个三对角阵。

2.2 上海森伯矩阵的QR分解

首先,如果H是一个上海森伯格阵,其QR分解为H=QR,那么RQ也是一个上海森伯格阵。证明省略。

这就是说,如果我们有一个对海森伯格阵的快速QR算法,那么在每次迭代中我们可以反复使用该算法。

我们采用Givens变换来进行QR分解。对于n阶上海森伯格阵H,从上到下对每相邻的两行使用Given变换,每次变换都消去对角线下方的一个元素。共n-1次变换。由于Givens变换是正交变换,那么这n-1次变换的乘积也是正交变换,变换后,
\(G^T_{n-1}*\dots *G^T_1 H\) 变成了上三角阵。其中\(G^T_i\)是第i次Givens行变换的矩阵表示。
根据QR算法,我们需要求RQ。因此在对H进行n-1次行变换后,再从左到右对每相邻的两列使用对应的Given变换的逆变换,同样一共也是n-1次变换:
\(RQ=G^T_{n-1}*\dots *G^T_1 H G_1*\dots *G_{n-1}\),这样我们就完成了一次迭代。

这个过程可以用下图表示:

QR分解过程,$G_{n-1}^T*\dots*G_1^TH=R$,其中红框代表当前需要考虑的H中的元素,这些元素将被更新。注意每次Givens变换将一个对角线下的元素变成0.

而R*Q的过程如下:

其中

$c_i=\frac{H(i)_{i,i}}{\sqrt{H(i)^2_{i,i}+H(i)^2_{i+1,i}}}$

$s_i=\frac{H(i)_{i+1,i}}{\sqrt{H(i)^2_{i,i}+H(i)^2_{i+1,i}}}$

$H(i)=G^T_{i-1}*\dots*G_1^TH$

将这个过程中所有产生的G乘起来就得到了H的特征向量。我们可以看到,每次迭代只需要O(n^2)的计算量,比施密特正交化快多了。

2.3 三对角阵的特征值分解

在lanczos方法中,我们得到的是一个对称三对角阵,是上海森伯格阵的特例。因此可以用上面所述方法计算特征值。值得注意的是,这里可以利用三对角阵的特性加速计算,在求第一个行变换G'1H的时候,可以只考虑H左上的2*3的小阵,而不是整个前两行。而接下来的每次行变换也只需考虑H的2*3的子阵。在列变换中,每次只需考虑H的3*2的子阵即可。这样一次QR迭代的复杂度只有O(n)了。

2.4 通过平移(shifts)和deflation加速上海森伯格矩阵的特征值分解

由于QR算法的收敛速度取决于特征值之间的差异,差别越大,QR收敛越快。这和幂迭代算法是一致的。那么怎么加快特征值之间的差异呢?通常的做法就是平移。

假设H有两个特征值$\lambda_1>\lambda_2\ge0$,那么QR迭代的速度为 $\left(\frac{\lambda_2}{\lambda_1}\right)^k$。假如$\delta$满足$\lambda_1>\lambda_2\ge\delta\ge0$,那么矩阵$H^*=H-\delta I$和H有相同的特征向量,其特征值为$\lambda_1-\delta>\lambda_2-\delta\ge0$,而求H^*的特征向量会很快,其收敛速度为$\left(\frac{\lambda_2-\delta}{\lambda_1-\delta}\right)^k$

基于这样的观察,在求海森伯格矩阵的特征值时,每次迭代我们进行如下QR分解

  $H-\delta I=QR$ (QR分解)
  $H=RQ+\delta I$
显然,这样的QR分解不改变H的特征向量,但是如果选取合适的 $\delta$收敛速度要快很多。特别地,如果$\delta$恰好取到某个的特征值时,那么一次迭代即可就可确定该特征值。见文献https://www.math.kth.se/na/SF2524/matber15/qrmethod.pdf中的lemma 2.3.2。事实上我们没那么幸运,但可以用每次迭代中,H对角线上的元素可以看作是特征值的估计。所以每次计算完RQ后,取对角线上固定某个位置的元素(比如始终取第1个)作为$\delta$。但如果矩阵存在一对特征值互为相反数,那么这种方法不能收敛。比如矩阵 $\left(\begin{matrix}0 & 1\\1 & 0\end{matrix}\right)$,因此更好的做法是Wilkinson shift,取H对角线上的2*2的方阵的特征值作为$\delta$。此外对复矩阵还有双位移方法(Double shift),这里不作详细的讨论,有兴趣的读者可以参考http://people.inf.ethz.ch/arbenz/ewp/Lnotes/chapter4.pdf


那么这样只能加速计算某个特征值,比如始终取对角线上的最后一个则加快了求解最小特征值的速度。如何加速求解其他特征值的速度呢?这里就用到了deflation。

抱歉我不知道怎么翻译这个词deflation,因此就用英文了。

Deflation是指在迭代过程中,如果对角线上某个位置的下一行几乎为0的时候,说明该位置上的值很接近于特征值了,此时将改位置所对应的行和列从矩阵中刨去,对剩下的矩阵进行计算。

一般的步骤如下,取对角线最右下的元素做deflation,直到该元素左边的元素几乎为0时,这时最小特征值已经求出,将最后一行一列去掉,对剩下的矩阵继续计算。如此反复,deflate n-1次之后,所有特征值全部求出。

2.5 QR算法的C++实现

最后,附上我的QR算法的C++实现。这个算法实现了这篇文章中的所有内容,包括实对称阵到上海森伯格阵的转化(hessenburgReduction函数),上海森伯格阵特征值分解的QR算法(QRHessenberg函数),三对角阵的特征值分解的QR算法(QRTridiagonal函数),每个QR算法中都用了shift和deflation加速,采用的是Wilkinson shift。在Lanczos算法中,只有QRTridiagonal函数被调用。当然如果不用lanczos方法三对角化,也可以通过hessenburgReduction函数和QRHessenberg函数来进行QR迭代求解特征值分解。具体代码参见lanczos算法及C++实现(〇)C++代码

3. 参考文献

https://www.math.kth.se/na/SF2524/matber15/qrmethod.pdf

http://people.inf.ethz.ch/arbenz/ewp/Lnotes/chapter4.pdf

 

附录

附录 A QR算法等价于并行幂迭代算法(Simultaneous Iteration)

QR算法实际上等价于并行幂迭代算法 (Simultaneous Iteration)。在幂迭代中,如果我们用一组线性独立的向量而不是一个向量与X反复相乘,则最终得到的将是一组线性独立的向量,对其正交化后,可以 得到X的特征向量。但实际上由于幂迭代的性质,这些向量快速的收敛到主特征向量,而其他的特征向量被快速丢弃。为了保留较小特征向量的成分,每次与X相乘 后,都做一次正交化,那么这样最终这些向量将收敛到X的不同的特征向量。如果一开始选取的一组向量是单位阵,那么这种并行的幂迭代算法,实际上就是QR算 法。为什么这么说呢?我们可以看到,第一迭代,这组向量相乘X,得到X,然后正交化。而正交化实际上就是QR分解:$X=Q_1*R_1$,接着第二次迭代,我们用X乘$Q_1$并正交化:$X*Q_1=Q_2*R_2$,如此我们得到

\(X=Q_1R_1\)

\(XQ_1=Q_2R_2\)

\(XQ_2=Q_3R_3\)

...

比较一下QR算法

\(X=Q_1R_1\)

\(R_1Q_1=Q_2R_2\)

\(R_2Q_2=Q_3R_3\)

...

用数学归纳法可以证明QR算法中的
\(R_k\)
和并行幂迭代中的
\(R_k\)
相同。因为当k=1时,显然成立,当k=2时
并行幂迭代中
\(Q_1R_1Q_1=Q_2R_2\)
而QR算法中
\(R_1Q_1=Q_2R_2\)
由于
\(Q_1\)
是正交阵,一个矩阵在进行行正交变换(左乘正交阵)后,其QR分解中的R不变。因此两者的
\(R_2\)相同

以此类推。可以证明两个算法中所有的
\(R_k\)相同

然后,我们可以得到在并行幂迭代中,

\(Q_k=X^k*R_k^{-1}*R_{k-1}^{-1}*\dots*R_1^{-1}\)

而QR算法中

\(Q_k=R_{k-1}*\dots*R_1*X*R_k^{-1}*R_{k-1}^{-1}*\dots*R_1^{-1}\)

于是,可以得到两个算法中
\(Q_k\)的联系:

\(Q^{\mathrm{Simultaneous Iteration}}_k=Q_1^{QR}*\dots*Q_k^{QR}\)

 

附录 B Householder变换

又名Householder Reflections,顾名思义,是一种镜像变换。如图,假设给定一个点x和一个过原点的平面(虚线),平面用一个垂直于它的单位向量v表示,那么对x的Householder变换就是求x关于平面v的镜像点x'。现在我们来看怎么计算x'

首先$0$指向$x$的向量在向量$v$上的投影为 $v*v^Tx$ ,在平面v上的投影为$x-v*v^Tx$。所以其镜像点x'的坐标为$x-2v*v^Tx = (I-2vv^T)x$

这里矩阵$I-2vv^T$就是一个householder矩阵,是一个对称的正交矩阵,有两个特征值,1和-1

 

现在我们考虑如何利用householder变换将一个向量变x成只有一个非0元素的向量,如下图所示

关键就是求那个平面,即v向量。由图可得出x-x'平行于v,因此我们有

$v=\frac{u}{\|u\|}$

其中

$u=x-\left(\begin{matrix} \|x\| \\ 0 \\0 \\0 \\ \dots\end{matrix}\right)$

 

附录 C Givens变换

又名Givens旋转,顾名思义,是对一个向量进行旋转变换,不改变向量的长度,因此是一个正交变换,其逆变换就是反方向旋转回去。

如图所示,我们考虑一个二维向量$v=\left(\begin{matrix}a\\b\end{matrix}\right)$,将它逆时针旋转$\theta$角度后,求所得到的坐标。


这里我们可以将向量重新表示为\(v=|v|\left(\begin{matrix}cos(\alpha)\\sin(\alpha)\end{matrix}\right)\),其中\(\alpha\)是向量v与x坐标轴的夹角。


这样变换后的坐标为

$w=|v|\left(\begin{matrix}cos(\alpha+\theta)\\sin(\alpha+\theta)\end{matrix}\right)$

     $=|v|\left(\begin{matrix}cos(\alpha)cos(\theta)-sin(\alpha)sin(\theta)\\sin(\alpha)cos(\theta) + sin(\theta)cos(\alpha)\end{matrix}\right)$

     $= \left(\begin{matrix}cos(\theta) & -sin(\theta)\\sin(\theta) & cos(\theta)\end{matrix}\right)*|v|\left(\begin{matrix}cos(\alpha)\\sin(\alpha)\end{matrix}\right) $

     $= \left(\begin{matrix}cos(\theta) & -sin(\theta)\\sin(\theta) & cos(\theta)\end{matrix}\right)v$

 

其中$G= \left(\begin{matrix}cos(\theta) & -sin(\theta)\\sin(\theta) & cos(\theta)\end{matrix}\right)$为Givens变换矩阵,是一个正交阵,其逆阵为

 

$G^T= \left(\begin{matrix}cos(\theta) & sin(\theta)\\-sin(\theta) & cos(\theta)\end{matrix}\right)$

 

本文属作者原创,转载请注明出处:

http://www.cnblogs.com/qxred/p/qralgorithm.html

 

本系列目录:

lanczos算法及C++实现(〇)C++代码

lanczos算法及C++实现(一)框架及简单实现

lanczos算法及C++实现(二)实对称阵奇异值分解的QR算法

lanczos算法及C++实现(三)实对称三对角阵特征值分解的分治算法

 

posted @ 2016-09-21 03:05  qxred  阅读(9547)  评论(0编辑  收藏  举报