显示标签为“LU”的博文。显示所有博文
显示标签为“LU”的博文。显示所有博文

2014年7月31日星期四

LU分解

1/6 LU 分解

LU 分解可以写成A = LU,这里的L代表下三角矩阵,U代表上三角矩阵。对应的matlab代码如下:

function[L, U] =zlu(A)

% ZLU - LU decomposition for matrix A

% work as gauss elimination

[m, n] = size(A);

if m ~= n

error('Error, current time only support square matrix');

end

L = zeros(n);

U = zeros(n);

for k = 1:n-1

gauss_vector = A(:,k);

gauss_vector(k+1:end) = gauss_vector(k+1:end) ./ gauss_vector(k);

gauss_vector(1:k) = zeros(k,1);

L(:,k) = gauss_vector;

L(k,k) = 1;

for l=k+1:n

A(l,:) = A(l,:) - gauss_vector(l)*A(k,:);

end

end

U = A;

这段代码的目的非常简单,就是使用高斯消元法给出L,U。但是计算的稳定性非常不好,这点可以通过这段代码的分解结果和matlab自带lu的分解结果相比较得出。比较的方法非常简单:就是计算l*u与原始矩阵想减之后的Frobinus范数大小,使用如下的代码做出两个结果的比较:

n = 1000;

my_error = zeros(1, 1000);

sys_error = zeros(1, 1000);

for i = 1:n

test = randn(5);

[zl, zu] = zlu(test);

[l, u] = lu(test);

my_error(i) = norm(zl*zu - test, 'fro');

sys_error(i) = norm(l*u - test, 'fro');

end

disp(mean(my_error));

disp(var(my_error));

disp(mean(sys_error));

disp(var(sys_error));

在这段代码里面,随机的生成一个5x5的符合高斯分布的矩阵,然后使用自己写的lu分解和matlab自带的lu分解分别给出L和U,再计算norm(L*U - test),从这里就可以看出我们自己计算出来的结果精度和matlab自带的lu真实的差异了。这个差异就体现为这些值的均值和方差。结果如下:

mean of my lu : 13.313846
variance of my lu : 43622.114147
mean of matlab lu : 0.000000
variance of matlab lu : 0.000000

从这个结果可以看出,我们自己写的lu分解的结果在均值和方差上比matlab自带的差了很多。个人认为原因有两点:第一个方法的原因,matlab给出的结果是pivoted LU,第二个是因为实现的原因,matlab基于成熟的LAPACK,肯定会比自己写的更好了。

这一步使用PA = LU来完成LU分解。代码如下:

function [P, L, U] = zplu(A)

% pivoted LU decompositon P*A = L*U

[m, n] = size(A);

if m ~= n

error('zplu:test', 'current time only support square matrix');

end

P = eye(n);

L = zeros(n, n);

for k = 1:n-1

%find the largest element in k column of A from row k to n

[max_value, max_index] = max(A(k:end, k));

max_index = max_index + k - 1;

if max_index ~= k

A([k max_index], :) = A([max_index k], :);

P([k max_index], :) = P([max_index k], :);

L([k max_index], :) = L([max_index k], :);

end

if A(k,k) ~= 0

gauss_vector = A(:,k);

gauss_vector(k+1:end) = gauss_vector(k+1:end) ./ gauss_vector(k);

gauss_vector(1:k) = zeros(k,1);

L(:,k) = gauss_vector;

L(k, k) = 1;

for l=k+1:n

A(l,:) = A(l,:) - gauss_vector(l)*A(k,:);

end

end

end

U = triu(A);

下面是运行前面检测程序的输出:

mean of my lu : 7.803258
variance of my lu : 1450.332510
mean of matlab lu : 0.000000
variance of matlab lu : 0.000000

两个结果相比较可以看到,Matlab的lu一样的稳定,但是使用pivot来调整矩阵A的次序可以极大的提高LU分解的稳定度,这个可以从下降了非常多的方差可以看出。

pivot LU是从k列的k+1到n个元素种选择最大的一个,调换到第k个位置。从我个人的角度理解,除以最大的元素使得高斯变换矩阵中非对角元素全部小于1。由于计算机种存储浮点数的机制,绝对值越靠近0,其精度越高。所以使用pivot这种方法可以极大的提高LU分解的稳定程度。但是也需要指出,使用pivot并不一定能提高LU分解的精度,对于特定的矩阵,不使用pivot说不定可以获得更好的性能。

为了进一步提高提高LU分解的稳定性,可以使用full pivoted LU。分解公式:P*A*Q = L * U; 对应的Matlab代码如下:

function [P, Q, L, U] = zflu(A)

%full pivoted LU decomposition

%

% full pivoted LU decomposition

[m, n] = size(A);

if m ~= n

error('current only support square matrix')

end

P = eye(n);

Q = eye(n);

for k=1:n-1

%find the larget element in A(k:n,k:n)

[max_value, row_index] = max(A(k:n, k:n));

[max_value, col_index] = max(max_value);

real_row = k-1 + row_index(col_index);

real_col = k-1 + col_index;

%exchange the row and column of matrix A

if real_row ~= k

A([k real_row],:) = A([real_row k], :);

P([k real_row],:) = P([real_row k], :);

end

if real_col ~= k

A(:, [k real_col]) = A(:, [real_col k]);

Q(:, [k real_col]) = Q(:, [real_col k]);

end

if A(k, k) ~= 0

rows = k+1:n;

A(rows, k) = A(rows, k) ./ A(k, k);

A(rows, rows) = A(rows, rows) - A(rows, k)*A(k, rows);

end

end

L = tril(A);

for k=1:n

L(k, k) = 1;

end

U = triu(A);

跑完test之后的结果如下:

mean of my lu : 7.77222e-16
variance of my lu : 4.3478e-29
mean of matlab lu : 3.69764e-16
variance of matlab lu : 2.03659e-32

可以看到使用full pivoted LU 分解可以在很大程度上保证分解的稳定性,即便是使用我们自己写的代码。但是即便如此,仍然推荐使用LAPACK中的代码,因为那里面的代码是经过严格的测试和分析的,在各种一场情况下应该都有很好的表现。

这里所介绍的LU分解可以使用另外一种基于GaxPy形式的运行,将在下面介绍。

LU分解

接着上次LU分解的讲解,这次给出使用不同的计算LU分解的方法,这种方法称为基于GaxPy的计算方法。这里需要了解lapapck中的一些函数。lapack中有一个函数名为gaxpy,所对应的矩阵计算公式是:x = Gx + y; 对应的Matlab代码如下:

function[L, U] =zgaxpylu(A)

%calculate LU decomposition based on Gaxpy operation

%the same way as zlu.m but differnt approach

[m, n] = size(A);

if m ~= n

error('current support square matrix only')

end

L = eye(n);

U = eye(n);

for k = 1:n

v = zeros(n, 1);

if k == 1

v(k:end) = A(k:end, k);

else

U(1:k-1,k) = L(1:k-1,1:k-1)\A(1:k-1,k);

v(k:end) = A(k:end, k) - L(k:end, 1:k-1)*U(1:k-1,k);

end

if k < n

L(k+1:end,k) = v(k+1:end)/v(k);

L(k, k) = 1;

end

U(k, k) = v(k);

end

将这段代码运行1500次,每次使用随机生成的高斯矩阵并和Matlab的lu分解结果对比,统计数据如下:

mean of my lu : 1.7676e-14

variance of my lu : 3.07075e-25

mean of matlab lu : 3.70416e-16

variance of matlab lu : 2.14989e-32

和前一种方法不同,目前的这个方法显式的计算出L和U,而在前面的方法是计算出L,然后就可以拿到U。

使用部分选主元的方法进行LU分解,对应如下代码:

function [P, L, U] = zgaxpyplu(A)

%compute LU decomposition based on Gaxpy operation with pivoting

%aimed at improve the stability of LU decomposition

[m, n] = size(A);

if m ~= n

error('current support square matrix only')

end

P = eye(n);

L = eye(n);

U = zeros(n);

for k = 1:n

v = zeros(n, 1);

if k == 1

v(k:end) = A(k:end, 1);

else

U(1:k-1, k) = L(1:k-1, 1:k-1) \ A(1:k-1, k);

v(k:n) = A(k:n, k) - L(k:n, 1:k-1)*U(1:k-1, k);

end

%find the largest element in v(k:end)

[max_value, max_index] = max(v(k:end));

max_index = max_index + k - 1;

if max_index ~= k

%exchange the max_index-th row and k-th row

A([k max_index], k+1:n) = A([max_index k], k+1:n);

%exchange the max_index-th row and k-th row in L

if k > 1

L([k max_index], 1:k-1) = L([max_index k], 1:k-1);

end

P([k max_index], :) = P([max_index k], :);

v([k max_index]) = v([max_index k]);

end

if (abs(v(k)) > 1e-6) && (k < n)

v(k+1:end) = v(k+1:end) / v(k);

L(k+1:end, k) = v(k+1:end);

end

U(k, k) = v(k);

end

运行这段代码1500次,每次都使用随机生成的矩阵和Matlab的LU分解做对比,结果如下:

mean of my lu : 2.02547e-15
variance of my lu : 9.06349e-29
mean of matlab lu : 3.65765e-16
variance of matlab lu : 1.98304e-32

将每个算法和同类型的算法相比,使用gaxpy的算法都比之前的方法要好。但是现在有一个问题是使用pivot的算法居然比不使用pivot的算法没有足够显著的改进,当然这也证明了使用基于gaxpy运算可以提高LU分解的稳定性。

总结:LU分解是用于求解线性方程组最基本的矩阵分解方法,对于LU分解可以使用高斯消元法,也可以使用基于gaxpy的运算方法。但是从目前的结果来看,使用gaxpy运算可以极大的提高LU分解的稳定性。但是作为成熟的方法,使用lapack中对应的方法是最好的选择。