A = L U A=LU A=LU分解算法:
1. u 1 i = a 1 i , ( i = 1 , 2 , ⋯   , n ) ; l i 1 = a i 1 / u 11 , ( i = 2 , 3 , ⋯   , n ) 1. u_{1i}=a_{1i} ,(i=1,2,\cdots,n) ; l_{i1}=a_{i1}/u_{11} ,(i=2,3,\cdots,n) 1.u1i=a1i,(i=1,2,⋯,n);li1=ai1/u11,(i=2,3,⋯,n)
计算 U U U的第 r r r行, L L L的第 r r r列元素 ( r = 2 , 3 , ⋯   , n ) (r=2,3,\cdots,n) (r=2,3,⋯,n):
2. u r i = a r i − ∑ k = 1 r − 1 l r k u k r , ( i = r , r + 1 , ⋯   , n ) 2. u_{ri}=a_{ri}-\sum_{k=1}^{r-1}l_{rk}u_{kr},(i=r,r+1,\cdots,n) 2.uri=ari−∑k=1r−1lrkukr,(i=r,r+1,⋯,n)
3. l i r = ( a i r − ∑ k = 1 r − 1 l i k u k r ) / u r r , ( i = r + 1 , ⋯   , n , 且 r ≠ n ) 3. l_{ir}=(a_{ir}-\sum_{k=1}^{r-1}l_{ik}u_{kr})/u_{rr},(i=r+1,\cdots,n,且r\neq n) 3.lir=(air−∑k=1r−1likukr)/urr,(i=r+1,⋯,n,且r̸=n)
求解 L y = b , U x = y Ly=b,Ux=y Ly=b,Ux=y的计算公式:
4. y 1 = b 1 ; y i = b i − ∑ k = 1 i − 1 l i k y k , ( i = 2 , 3 , ⋯   , n ) 4. y1=b1;y_i=b_i-\sum_{k=1}^{i-1}l_{ik}y_k,(i=2,3,\cdots,n) 4.y1=b1;yi=bi−∑k=1i−1likyk,(i=2,3,⋯,n)
5. x n = y n / u n n ; x i = ( y i − ∑ k = i + 1 n u i k x k ) / u i i , ( i = n − 1 , n − 2 , ⋯   , 1 ) 5. x_n=y_n/u_{nn};x_i=(y_i-\sum_{k=i+1}^{n}u_{ik}x_k)/u_{ii},(i=n-1,n-2,\cdots,1) 5.xn=yn/unn;xi=(yi−∑k=i+1nuikxk)/uii,(i=n−1,n−2,⋯,1)
其中值得注意的是:LU分解所用到的数学知识很基础并没有太大难度,只不过是矩阵乘法的知识以及两个矩阵相等时其对应元素也相等的知识。
用LU分解算法求解给定的线性方程组 A x ⃗ = b ⃗ A\vec{x}=\vec{b} Ax=b,其中函数接口定义为:
bool Direct( int n, double a[][MAX_SIZE], double b[] )
在 D i r e c t Direct Direct的接口定义中, n n n为矩阵 a a a的维数,MAX_SIZE是由裁判程序定义的矩阵最大维数, b ⃗ \vec{b} b是方程组中的常向量,求得的解将存储在 b ⃗ \vec{b} b中返回。本题目要求先用杜利特尔分解,再求解两个三角型方程组。函数返回布尔型值,当求解成功时返回TRUE,否则返回FALSE。
其中裁判程序的主函数为:
int main()
{
int n, i, j;
double a[MAX_SIZE][MAX_SIZE], b[MAX_SIZE];
while ( scanf("%d", &n) != EOF ) { /* 读取裁判测试用例 */
for ( i=0; i<n; i++ ) {
for ( j=0; j<n; j++ )
scanf("%lf", &a[i][j]);
scanf("%lf", &b[i]);
}
/*--- 输出直接法的解 ---*/
if ( Direct(n, a, b) ) {
printf("Result of direct method:\n");
for ( j=0; j<n; j++ )
printf("%.8lf\n", b[j]);
}
else
printf("Doolittle factorization failed.\n");
printf("\n");
}
return 0;
}
L U LU LU分解实验中最重要的函数为 D i r e c t Direct Direct函数,下面将给出实现 D i r e c t Direct Direct函数的步骤。
由于 D i r e c t Direct Direct函数返回 b o o l bool bool值,所以首先定义一个 b o o l bool bool类型的变量“ f l a g flag flag”并初始化为 t r u e true true,作为判断矩阵A是否能进行 L U LU LU分解的标志;其次,由于对 A A A进行 L U LU LU分解的矩阵 L L L为单位下三角矩阵, U U U为上三角矩阵,所以LU可以同时存在并覆盖原始的A矩阵,故无需创建新的矩阵 L L L和 U U U;最后需建立一个 d o u b l e double double类型的y数组存放 L y ⃗ = b ⃗ L\vec{y}=\vec{b} Ly=b中 y ⃗ \vec{y} y的值。
由于后续的计算需要用到L矩阵的第一列,故需先进行赋值;而由于U矩阵的第一行与A矩阵的第一行相同,故可以不做赋值处理。
此处的赋值操作与“算法理论”部分介绍的思想一致。
由于只有非奇异的矩阵 A A A才能进行 L U LU LU分解,但在实验过程中,计算矩阵 A A A的行列式是否非0或判断矩阵 A A A的顺序主子式是否都大于0相对而言都比较困难,所以要采取其他方式进行判断。
在求解 l i r l_{ir} lir的过程中,由于
l i r = ( a i r − ∑ k = 1 r − 1 l i k u k r ) / u r r , ( i = r + 1 , ⋯   , n , 且 r ≠ n ) l_{ir}=(a_{ir}-\sum_{k=1}^{r-1}l_{ik}u_{kr})/u_{rr},(i=r+1,\cdots,n,且r\neq n) lir=(air−∑k=1r−1likukr)/urr,(i=r+1,⋯,n,且r̸=n)
故只需最后对 u r r u_{rr} urr做出判断即可。当 u r r = 0 u_{rr}=0 urr=0时将 f l a g flag flag置为 f a l s e false false,并直接返回 f l a g flag flag,不再进行后续的“Step5”和“Step6”操作。
由于求解出的 x ⃗ \vec{x} x,可以存储在b数组中,所以在“Step1:定义变量”中并未定义x数组,求出的解 x ⃗ \vec{x} x即全部存储在b数组中。
#include<stdio.h>
#include<math.h>
#include<stdbool.h>
#define MAX_SIZE 100 /* 矩阵最大维数 */
#define ZERO 0.000000001 /* 当一个正数小于ZERO就认为该数是0 */
bool Direct( int n, double a[][MAX_SIZE], double b[] )
{
bool flag = true;
double y[n];
int r;
int i;
int k;
double sum_u, sum_l;
for(r=1;r<n;r++)
a[r][0] = a[r][0]/a[0][0];
for(r=1;r<n;r++)
{
for(i=r;i<n;i++)
{
sum_u = 0;
for(k=0;k<r;k++)
sum_u += a[r][k]*a[k][i];
a[r][i] = a[r][i]-sum_u;
}
for(i=r+1;i<n&&r!=n-1;i++)
{
sum_l = 0;
for(k=0;k<r;k++)
sum_l += a[i][k]*a[k][r];
a[i][r] = (a[i][r]-sum_l)/a[r][r];
}
}
//--------------------------------------------------------------------------//
for(r=0;r<n;r++)
{
if(a[r][r]==0) //等价于判断矩阵的顺序主子式是否为0
{
flag = false;
return flag;
}
}
//--------------------------------------------------------------------------//
//--------------------------------------------------------------------------//
y[0] = b[0];
double sum;
for(i=1;i<n;i++)
{
sum = 0;
for(k=0;k<i;k++)
sum += a[i][k]*y[k];
y[i] = b[i]-sum;
}
//--------------------------------------------------------------------------//
//--------------------------------------------------------------------------//
b[n-1] = y[n-1]/a[n-1][n-1];
for(i=n-2;i>=0;i--)
{
sum = 0;
for(k=i+1;k<n;k++)
sum += a[i][k]*b[k];
b[i] = (y[i]-sum)/a[i][i];
}
return flag;
}
int main()
{
int n, i, j;
double a[MAX_SIZE][MAX_SIZE], b[MAX_SIZE];
while ( scanf("%d", &n) != EOF ) { /* 读取裁判测试用例 */
for ( i=0; i<n; i++ )
{
for ( j=0; j<n; j++ )
scanf("%lf", &a[i][j]);
scanf("%lf", &b[i]);
}
/*--- 输出直接法的解 ---*/
if ( Direct(n, a, b) )
{
printf("Result of direct method:\n");
for ( j=0; j<n; j++ )
printf("%.8lf\n", b[j]);
}
else
printf("Doolittle factorization failed.\n");
printf("\n");
}
return 0;
}
Input:
6
4 -1 0 -1 0 0 0
-1 4 -1 0 -1 0 5
0 -1 4 0 0 -1 0
-1 0 0 4 -1 0 6
0 -1 0 -1 4 -1 -2
0 0 -1 0 -1 4 6
Output:
Result of direct method:
1.00000000
2.00000000
1.00000000
2.00000000
1.00000000
2.00000000
//--------------------------------//
Input:
3
3 -1 0 1
3 6 0 0
3 3 0 4
Output:
Doolittle factorization failed.
//--------------------------------//
Input:
3
3 -1 3 1
3 6 3 0
3 3 3 4
Output:
Doolittle factorization failed.
//--------------------------------//
Input:
7
1.0 0.0 2.0 0.0 3.0 0.0 4.0 3
3.0 -1.0 0.5 8.0 2.2 1.6 0.0 8
0.0 0.0 0.0 4.5 3.2 2.0 1.0 2
2.0 3.0 5.0 0.0 0.0 0.0 2.0 4
-2.0 -3.0 1.0 1.0 0.0 0.0 3.3 1
2.5 4.5 0.0 0.0 1.0 0.0 0.0 -2
-0.5 -1.5 3.0 2.0 0.0 1.0 -1.0 5
Output:
Doolittle factorization failed.
//--------------------------------//
Input:
3
1.00000000 0.50000000 0.33333333 1.0
0.50000000 0.33333333 0.25000000 1.0
0.33333333 0.25000000 0.20000000 1.0
Output:
Result of direct method:
3.00000408
-24.00002076
30.00001920
//--------------------------------//
Input:
4
1.00000000 0.50000000 0.33333333 0.25000000 1.0
0.50000000 0.33333333 0.25000000 0.20000000 1.0
0.33333333 0.25000000 0.20000000 0.16666667 1.0
0.25000000 0.20000000 0.16666667 0.14285714 1.0
Output:
Result of direct method:
-4.00028881
60.00328814
-180.00799473
140.00523622
//--------------------------------//
欢迎批评指正!