甲乙小朋友的房子

甲乙小朋友很笨,但甲乙小朋友不会放弃

0%

矩阵LU分解及其实现

LU分解:可以将一个矩阵分解为一个单位下三角矩阵和一个上三角矩阵乘积(有时是它们和一个置换矩阵的乘积)。LU分解主要应用在数值分析中,用来解线性方程、求反矩阵或计算行列式。

LU分解条件

如果n阶方阵A的各阶顺序主子式 ,即A的各阶顺序主子矩阵Ak都可逆,则存在唯一的单位下三角矩阵L与唯一的非奇异上三角矩阵U,使得A=LU

其中k阶顺序主子式指

LU分解方法

由于L存在可逆矩阵L',即LL'=E 则L'A=LL'U=U 因此得出一般的分解方法: 通过对(A,E)做初等行变换得到(U,L'),再由L'得到L.

其中:

  1. L是单位下三角矩阵(对角线上的系数都为1的下三角矩阵);L'也是单位上三角矩阵;
  2. U是非奇异上三角矩阵;

直接消去法的矩阵解释

计算过程如图所示:

目的:将[A|E]-->[U|L]形式,其中U是上三角矩阵

过程:每轮将一列的下三角消为0,就相当于给A左乘一个下三角单位矩阵Lk(对角线为1,保证变换后对角线元素不变)。

第一轮消元:将第一列的下三角消为0,相当于对A1左乘矩阵L1,即: 其中, 我们注意到li1恰好使得第i行的第一列元素变为零,即ai1-li1*a11=0。

第二轮消元:将第二列的下三角消为0,对应于

第k轮消元: 其中,

把k轮消元相乘,即: 整个消元过程为

总结 初始(第一步):

迭代第r步: 即 ur=f(u1,u2,...,u(r-1),l1,l2,...,l(r-1)),第r项由前r-1项计算得到 lr=g(u1,u2,...,u(r-1),l1,l2,...,l(r-1))

计算顺序:

需要注意的是,我们在计算时需要判断本阶的顺序主子式是否为零。

代码实现 备注:其中的piovt暂时没有卵用。

输入矩阵:

bool lu(double *a,int *pivot,int n){//矩阵是n*n的
double test = det(a,r,n);//检验本阶顺序主子式是否为0
 if(test==0){
    return true;
 }
//数组a在内存中按行优先次序存放
//pivot:输出参数
//pivot[0,n)中存放主元的位置排列
//成功时返回false,否则返回ture
//int len = sizeof(a)/sizeof(a[0]);
double *U,*L;
U = new double[n*n];
L = new double[n*n];
set0(U,n);
set0(L,n);
//先算urj(固定r,计算完所有的urj),再算ljr(固定i,计算完所有的ljr)
for(int r=0;r<n;r++){
    //计算urj(固定r,计算完所有的urj)
    //计算ljr(固定i,计算完所有的ljr)
    for(int j=r;j<n;j++){
        double sum_lu1=0;
        double sum_lu2=0;
        for(int k=0;k<r;k++){
            sum_lu1 = sum_lu1+L[r*n+k]*U[k*n+j];
            sum_lu2 = sum_lu2+L[j*n+k]*U[k*n+r];
        }
        U[r*n+j]=a[r*n+j]-sum_lu1;
        if(j==r){
            L[j*n+r]=1;
        }else{
            L[j*n+r]=a[j*n+r]-sum_lu2;
            L[j*n+r]=L[j*n+r]/U[r*n+r];
        }
    }
}
return false;
}

结果:

不知道为什么,我的答案与网上的之前学长做过的这道题的答案统统不一样。仔细检查后还是查不出来。又用Matlab验证了一下LU分解,MATLAB跑的结果为: 与我自己的L矩阵一致。决定还是相信自己。

列主元高斯消去法

主要思想是在每次计算第r行时,选取当前第r列下的最大的a[i,r]的所在的第i行作为主元,即将第i行与第r行交换,再进行计算。

主要理论见参考文献-北邮理学院数值分析课件chapter5.

代码:

#include <iostream>
#include<cstdio>
#include<cstdlib>
#include<iomanip>
#define LIM -100000000
using namespace std;
double *luReult;
void printmat(double *mat,int n,string s){
    std::cout<<endl<<endl<<s<<endl;
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            printf("%.5f\t",mat[i*n+j]);
        }
        std::cout<<endl;
    }
}
void printarr(int *mat,int n,string s){
    std::cout<<endl<<endl<<s<<endl;
    for(int i=0;i<n;i++){
        std::cout<<mat[i]<<"\t";
        //printf("%s\t",mat[i]);
    }
}
int findMainNumber(double *a,int n,int r){
    float maxa=-99999;
    int maxaIdx=-1;
    //printmat(a,n,"a is now :");
    for(int i=r;i<n;i++){
        if(a[i*n+r]>maxa){
            maxa=a[i*n+r];
            maxaIdx=i;
        }
    }
    return maxaIdx;
}
void exchange(double *a,int n,int e,int r){
    float temp=0;
    for(int i=0;i<n;i++){
        temp = a[r*n+i];
        a[r*n+i]=a[e*n+i];
        a[e*n+i]=temp;
    }
}
bool lu(double* a, int* pivot, int n)//矩阵LU分解
{
      for(int k=0;k<n;k++){
        //寻找第k列的主元
        int p = findMainNumber(a,n,k);
        exchange(a,n,p,k);//交换k行和p行
        pivot[k]=p;//记录置换矩阵p
        if(a[k*n+k]!=0){
                for(int i=k+1;i<n;i++){//部分下三角L
                    a[i*n+k]=a[i*n+k]/a[k*n+k];
                }
                for(int i=k+1;i<n;i++){//计算上三角U
                    for(int j=k+1;j<n;j++){
                        a[i*n+j]=a[i*n+j]-a[i*n+k]*a[k*n+j];
                    }
                }
        }else{
            return true;//矩阵奇异
        }
      }
      /*
      //计算下三角L
      double temp=0;
       for(int i=0; i<n-2; i++)//i行k列
            for(int k=i+1; k<n-1;k++)
            {
                    temp=a[n*pivot[k] + i];
                    a[n*pivot[k] + i]=a[k*n + i];
                    a[k*n + i]=temp;
            }
        */
      luReult=a;
      printmat(a,n,"lu");
      return false ;
}
double radio(int a,int b){
    return (double)(a)/(double)(b);
}
void buildHilbert(double *a,double *b,int n){
    for(int r=0;r<n;r++){
        for(int j=0;j<n;j++){
            a[r*n+j]=radio(1,j+r+1);
            b[r]=b[r]+a[r*n+j];
        }
    }
}
void exchangeb(double *b,int n,int r,int e){
    double temp=0;
    temp=b[e];
    b[e]=b[r];
    b[r]=temp;
    /*r(int i=0;i<n;i++){
        temp=b[r*n+i];
        b[r*n+i]=b[e*n+i];
        b[e*n+i]=temp;
    }*/
}

int main()
{
    int n=6;//矩阵是n*n的
    double *b=new double[n];
    double *a=new double[n*n];
    /*double input[n*n]={0.001,1.00,1.00,2.00};
    a=input;
    double inputb[n]={1.00,3.00};
    b=inputb;*/
    buildHilbert(a,b,n);
    printmat(a,n,"a:");
    int *pivot=new int[n*n];
    luReult=new double[n*n];
    lu(a,pivot,n);
    printarr(pivot,n,"p:");
    //cout << "Hello world!" << endl;
    return 0;
}

输出:

参考文献

  1. LU 分解
  2. 矩阵分析——LU分解