R 有一个qr()
函数,它使用 LINPACK 或 LAPACK 执行 QR 分解(根据我的经验,后者快 5%)。返回的主要对象是一个矩阵“qr”,它包含在上三角矩阵 R(即R=qr[upper.tri(qr)]
)中。到目前为止,一切都很好。qr 的下三角形部分包含“紧凑形式”的 Q。可以使用 qr 从 qr 分解中提取qr.Q()
Q。我想找到 的倒数qr.Q()
。换句话说,我确实有 Q 和 R,并且想把它们放在一个“qr”对象中。R 是微不足道的,但 Q 不是。目标是应用于它,这比在大型系统上qr.solve()
要快得多。solve()
2 回答
介绍
默认情况下,R 使用LINPACK例程或指定时使用 LAPACK例程来计算 QR 分解。两个例程都使用 Householder 反射计算分解。一个 mxn 矩阵 A 被分解为一个 mxn 经济尺寸正交矩阵 (Q) 和一个 nxn 上三角矩阵 (R),因为 A = QR,其中 Q 可以通过 t 个 Householder 反射矩阵的乘积来计算,其中 t 是较小的m-1 和 n 的: Q = H 1 H 2 ...H t。dqrdc
DGEQP3
每个反射矩阵 H i可以由长度-(m-i+1) 向量表示。例如,H 1需要一个长度为 m 的向量来进行紧凑存储。该向量的除一个条目之外的所有条目都放置在输入矩阵下三角形的第一列(对角线由 R 因子使用)。因此,每个反射都需要一个存储标量,这是由一个辅助向量提供的($qraux
在 R's 的结果中调用qr
)。
LINPACK 和 LAPACK 例程使用的紧凑表示不同。
LINPACK 方式
Householder 反射计算为 H i = I - v i v i T /p i,其中 I 是单位矩阵,p i是 中的对应条目$qraux
,vi如下:
- v我[1..i-1] = 0,
- v我[我] = p我
- v i [i+1:m] = A[i+1..m, i] (即调用后A的下三角的一列
qr
)
LINPACK 示例
让我们通过R 中 Wikipedia 上的 QR 分解文章中的示例来研究。
被分解的矩阵是
> A <- matrix(c(12, 6, -4, -51, 167, 24, 4, -68, -41), nrow=3)
> A
[,1] [,2] [,3]
[1,] 12 -51 4
[2,] 6 167 -68
[3,] -4 24 -41
我们进行分解,结果中最相关的部分如下所示:
> Aqr = qr(A)
> Aqr
$qr
[,1] [,2] [,3]
[1,] -14.0000000 -21.0000000 14
[2,] 0.4285714 -175.0000000 70
[3,] -0.2857143 0.1107692 -35
[snip...]
$qraux
[1] 1.857143 1.993846 35.000000
[snip...]
这种分解是(在幕后)通过计算两个 Householder 反射并将它们乘以 A 得到 R 来完成的。我们现在将根据 中的信息重新创建反射$qr
。
> p = Aqr$qraux # for convenience
> v1 <- matrix(c(p[1], Aqr$qr[2:3,1]))
> v1
[,1]
[1,] 1.8571429
[2,] 0.4285714
[3,] -0.2857143
> v2 <- matrix(c(0, p[2], Aqr$qr[3,2]))
> v2
[,1]
[1,] 0.0000000
[2,] 1.9938462
[3,] 0.1107692
> I = diag(3) # identity matrix
> H1 = I - v1 %*% t(v1)/p[1] # I - v1*v1^T/p[1]
> H2 = I - v2 %*% t(v2)/p[2] # I - v2*v2^T/p[2]
> Q = H1 %*% H2
> Q
[,1] [,2] [,3]
[1,] -0.8571429 0.3942857 0.33142857
[2,] -0.4285714 -0.9028571 -0.03428571
[3,] 0.2857143 -0.1714286 0.94285714
现在让我们验证上面计算的 Q 是否正确:
> qr.Q(Aqr)
[,1] [,2] [,3]
[1,] -0.8571429 0.3942857 0.33142857
[2,] -0.4285714 -0.9028571 -0.03428571
[3,] 0.2857143 -0.1714286 0.94285714
看起来不错!我们还可以验证 QR 等于 A。
> R = qr.R(Aqr) # extract R from Aqr$qr
> Q %*% R
[,1] [,2] [,3]
[1,] 12 -51 4
[2,] 6 167 -68
[3,] -4 24 -41
LAPACK 方式
Householder 反射计算为 H i = I - p i v i v i T,其中 I 是单位矩阵,p i是 中的对应条目$qraux
,vi如下:
- v我[1..i-1] = 0,
- v我[我] = 1
- v i [i+1:m] = A[i+1..m, i] (即调用后A的下三角的一列
qr
)
在 R 中使用 LAPACK 例程时还有另一个转折:使用列旋转,因此分解解决了一个不同的相关问题:AP = QR,其中 P 是置换矩阵。
LAPACK 示例
本节执行与之前相同的示例。
> A <- matrix(c(12, 6, -4, -51, 167, 24, 4, -68, -41), nrow=3)
> Bqr = qr(A, LAPACK=TRUE)
> Bqr
$qr
[,1] [,2] [,3]
[1,] 176.2554964 -71.1694118 1.668033
[2,] -0.7348557 35.4388886 -2.180855
[3,] -0.1056080 0.6859203 -13.728129
[snip...]
$qraux
[1] 1.289353 1.360094 0.000000
$pivot
[1] 2 3 1
attr(,"useLAPACK")
[1] TRUE
[snip...]
注意$pivot
字段;我们将回到这一点。Aqr
现在我们从信息中生成 Q。
> p = Bqr$qraux # for convenience
> v1 = matrix(c(1, Bqr$qr[2:3,1]))
> v1
[,1]
[1,] 1.0000000
[2,] -0.7348557
[3,] -0.1056080
> v2 = matrix(c(0, 1, Bqr$qr[3,2]))
> v2
[,1]
[1,] 0.0000000
[2,] 1.0000000
[3,] 0.6859203
> H1 = I - p[1]*v1 %*% t(v1) # I - p[1]*v1*v1^T
> H2 = I - p[2]*v2 %*% t(v2) # I - p[2]*v2*v2^T
> Q = H1 %*% H2
[,1] [,2] [,3]
[1,] -0.2893527 -0.46821615 -0.8348944
[2,] 0.9474882 -0.01602261 -0.3193891
[3,] 0.1361660 -0.88346868 0.4482655
再一次,上面计算的 Q 与 R 提供的 Q 一致。
> qr.Q(Bqr)
[,1] [,2] [,3]
[1,] -0.2893527 -0.46821615 -0.8348944
[2,] 0.9474882 -0.01602261 -0.3193891
[3,] 0.1361660 -0.88346868 0.4482655
最后,让我们计算 QR。
> R = qr.R(Bqr)
> Q %*% R
[,1] [,2] [,3]
[1,] -51 4 12
[2,] 167 -68 6
[3,] 24 -41 -4
注意到区别了吗?QR 是 A,其列按Bqr$pivot
上述顺序排列。
我已经研究了与 OP 要求相同的问题,但我认为这是不可能的。基本上,OP 问题是是否具有明确计算的 Q,是否可以恢复 H1 H2 ... Ht。如果不从头开始计算 QR,我认为这是不可能的,但我也很想知道是否有这样的解决方案。
我有一个与 OP 类似的问题,但在不同的上下文中,我的迭代算法需要通过添加列和/或行来改变矩阵 A。第一次,QR 是使用 DGEQRF 计算的,因此是紧凑的 LAPACK 格式。在矩阵 A 发生突变后,例如使用新行,我可以快速构建一组新的反射器或旋转器,它们将消除现有 R 的最低对角线的非零元素并构建一个新的 R 但现在我有一组 H1_old H2_old ... Hn_old 和 H1_new H2_new ... Hn_new(以及类似的 tau)不能混合成单个 QR 紧凑表示。我有两种可能性,也许 OP 也有两种可能性:
- 无论是在第一次计算时还是在每次更新后,始终保持 Q 和 R 显式分离,但代价是额外的触发器,但保持所需的内存有良好的界限。
- 坚持使用紧凑的 LAPACK 格式,但每次有新的更新时,都要保留所有这些迷你更新反射器集的列表。在解决系统问题时,会做一个大的 Q'*c 即 H1_u3*H2_u3*...*Hn_u3*H1_u2*H2_u2*...*Hn_u2*H1_u1*H2_u1...*Hn_u1*H1*H2* ...*Hn*c 其中 ui 是 QR 更新编号,这可能需要进行大量乘法运算和内存跟踪,但绝对是最快的方法。
David 的长长的回答基本上解释了紧凑 QR 格式是什么,但没有解释如何使用显式计算的 Q 和 R 作为输入的这种紧凑 QR 格式。