数值计算(一)之解线性方程组(高斯消去法,列选主元消去法,全选主元消去法,杜立特尔分解,克洛特分解,乔里斯基分解)

解线性方程组即解一个多元一次方程组,例如

  1. 目录
    1. 消去法

      分解法


 

消去法

原理

没有学过高级的解法也没关系,凭借我们初高中的知识足以解决这个问题

这是一个多元一次方程组,拥有n个未知量,也有n方程

我们可以通过对一个方程组进行同等变形后与其他方程组相加减,消去其他未知量

最后变为一个仅含有一个未知量的方程,求解后逐次回带,接触其他未知数

当我们接触了线性代数后,我们可以把这n个方程组的系数提取出来,与等号右边的值共同组成一个增广矩阵,对这个矩阵进行初等行列变换,然后求解。实质上和我们初高中的消元法是一样的。

例如这样一个方程组

我们构造这样一个 矩阵

数值计算这里把解线性方程组的方法系统化,分为消去法,分解法,迭代法。

1分解法

分解法是将我们上面构造的矩阵,进行初等行列变换后,变成单位上三角矩阵或者单位下三角矩阵,然后求解出一个未知数后逐个回带。

高斯消去法就是把矩阵变换为单位上三角矩阵

求解出X3以后逐个回带

 

列选消去法是在上面的基础上进行改进,每次选择对角线下那一列元素绝对值最大的元素,调整到对角线部分,在进行处理,用以减小误差。

全选消去法又是在列选消去法的基础上改进,每次选择从对角线元素开始的右下角矩形部分的最大值,把它放在对角线上,用以减小误差

由于全选消去法可能会交换列,进行列变换,改变变量的位置,所以用计算机求解时需要记录交换的情况,最后把未知数恢复为X1-Xn的顺序

 

高斯消去法

//高斯消去法
void GusElimination(double *p,int row)
{
    double coef;//系数,用于归一化和行处理
    int col=row+1;//保存增广矩阵的列数
    int i,j,k;//循环变量,指示行和列
    for(k=0;k<row;k++)//有多少个未知量即循环多少次
    {
        coef=*(p+k*col+k);
        for(j=0;j<col;j++)//对增广矩阵的第k行做归一化处理
            *(p+k*col+j)/=coef;

        for(i=k+1;i<row;i++)//消去k+1-row行对角线以下的元素
        {
            coef=(-1)*(*(p+i*col+k))/(*(p+k*col+k));//求与第k行的系数
            for(j=0;j<col;j++)
                *(p+i*col+j)+=(coef*(*(p+k*col+j)));//对k+1-row行所有元素都加上coef倍的第k行
        }//End for-i
    }//End for-k
    //求得单位上三角矩阵后,从下往上求解未知量
    for(i=row-1;i>=0;i--)
        for(j=row-1;j>i;j--)
            *(p+i*col+row)-=( *(p+j*col+row) ) * ( *(p+i*col+j) );
    //求解结果保存在增广矩阵的最后一列
}

列选消去法

//列选主元消去法
void ColumnElimination(double *p,int row)
{
    double coef,tmp,Max;
    int Maxrow,col=row+1;
    int i,j,k;

    for(k=0;k<row;k++)
    {
        Max=fabs(*(p+k*col+k));
        Maxrow=k;
        for(i=k+1;i<row;i++)//列选过程,找到当列绝对值最大的元素
            if( fabs(*(p+i*col+k)) > Max){
                Max=*(p+i*col+k); Maxrow=i;
            }//End if

        for(j=k;j<col;j++)
        {
            tmp = *(p+k*col+j);
            *(p+k*col+j)=*(p+Maxrow*col+j);
            *(p+Maxrow*col+j)=tmp;
        }//End for-j

        coef=*(p+k*col+k);//归一化处理,可以和上面步骤合并
        for(j=k;j<col;j++)
            *(p+k*col+j)/=coef;

        for(i=k+1;i<row;i++)//消去对角线下的元素
        {
            coef= (-1) * ( *(p+i*col+k) ) / ( *(p+k*col+k) );
            for(j=k;j<col;j++)
                *(p+i*col+j) += coef * ( *(p+k*col+j) );
        }//End for-i
    }//End for-k
    //求得单位上三角矩阵后,从下往上求解未知量
    for(i=row-1;i>=0;i--)
        for(j=row-1;j>i;j--)
            *(p+i*col+row)-=( *(p+j*col+row) ) * ( *(p+i*col+j) );
    //求解结果保存在增广矩阵最后一列
}

全选主元消去法

//全选主元消去法
void AllElimination(double *p,int row)
{
    double coef,tmp,Max;//coef表系数,tmp临时变量,Max用来保存绝对值最大元素
    int MaxCol,MaxRow,col=row+1;//MaxCol和MaxRow用来保存绝对值最大元素所在的行和列
    int i,j,k;
    int *a=(int *)malloc(row * sizeof(int));//申请一个临时数组用来保存交换记录
    for(i=0;i<row;i++)//对a数组进行初始化
        *(a+i)=i;

    for(k=0;k<row;k++)
    {
        Max=fabs(*(p+k*col+k));
        coef=*(p+k*col+k);
        MaxCol=k;   MaxRow=k;
        for(i=k;i<row;i++)//全选的过程
        {
            for(j=k;j<row;j++)
            {
                if( fabs(*(p+i*col+j)) >Max){
                    Max = fabs(*(p+i*col+j));
                    coef=*(p+i*col+j);
                    MaxRow=i;   MaxCol=j;
                }// End if
            }//End for-j
        }//End for-i

        if(MaxCol != k)//交换k和MaxCol两列,行i的范围从0到Row-1
        {
            for(i=0;i<row;i++)
            {
                tmp = *(p+i*col+k);
                *(p+i*col+k)=*(p+i*col+MaxCol);
                *(p+i*col+MaxCol)=tmp;
            }//End for-i
            //交换两列时需要记录
            tmp = *(a+k);//记录交换的是哪两列
            *(a+k)=*(a+MaxCol);
            *(a+MaxCol)=tmp;
        }//End if

        for(j=k;j<col;j++)
        {//一起处理需要先进行列交换(如果需要)
            if(MaxRow != k)
            {
                tmp=*(p+k*col+j);
                *(p+k*col+j)=*(p+MaxRow*col+j);
                *(p+MaxRow*col+j)=tmp;
            }//End if
            *(p+k*col+j)/=coef;
        }//End for-j

        for(i=k+1;i<row;i++)//消去对角线下的元素
        {
            coef= (-1) * ( *(p+i*col+k) ) / ( *(p+k*col+k) );
            for(j=k;j<col;j++)
                *(p+i*col+j) += coef * ( *(p+k*col+j) );
        }
    }
    //求得单位上三角矩阵后,从下往上求解未知量
    for(i=row-1;i>=0;i--)
        for(j=row-1;j>i;j--)
            *(p+i*col+row)-=( *(p+j*col+row) ) * ( *(p+i*col+j) );

    for(i=0;i<row-1;i++)
    {//对a进行冒泡排序,同时求解结果列进行相同操作
        for(j=0;j<row-i-1;j++)//通过排序来复原未知量原来的顺序
        {
            if( *(a+j) > *(a+j+1) )
            {
                tmp = *(a+j);
                *(a+j)=*(a+j+1);
                *(a+j+1)=tmp;

                tmp = *(p+j*col+row);
                *(p+j*col+row)= *(p+(j+1)*col+row);
                *(p+(j+1)*col+row)=tmp;
            }//End if
        }//End for-j
    }//End for-i
    free(a);
}

分解法

原理

消去法是增广矩阵化为单位上三角矩阵求解,除此之外,还有另外一种解题思路

那就是:将矩阵A分解成两个三角形矩阵L和U的乘积,其中L为下三角矩阵,U为上三角矩阵,则

可以证明,当系数矩阵的各阶顺序主子式不为0时,这样的分为有且仅有一组

 

在这种思路下,就有三种分解方法,

Doolitte分解法:L是单位下三角矩阵,U是上三角矩阵

Crout分解法:L是下三角矩阵,U是单位上三角矩阵

 

Cholesky分解法:L和U互为转置矩阵

要进行这样的分解,系数矩阵A就要满足是对称正定矩阵

对称:矩阵中的元素关于对角线对称

正定:矩阵所有的特征值都大于0

 

三角分解法不需要特别记住什么公式,只需要知道矩阵乘法的运算方式

进行一次逆运算,先计算某一个矩阵的行,在计算另外一个矩阵的列,以此往复。

杜立特尔分解法

//杜立特尔分解法,分解为单位下三角和上三角矩阵
void Doolittle(double *p,int row)
{
    int i,j,k,col=row+1;//增广矩阵的列
    double sum;//保存求和结果

    for(k=0;k<row;k++)//K表示循环次数,也指示矩阵的行和列,即沿对角线移动
    {//先计算U矩阵的K行
        for(j=k;j<row;j++)//控制U矩阵列的移动
        {//从对角线后面开始求,所以下标从k开始
            sum=0;//L矩阵固定K行
            for(i=0;i<k;i++)//乘到对角线前一个数字
                sum+= ( *(p+k*col+i) ) * ( *(p+i*col+j) );
            *(p+k*col+j) = ( *(p+k*col+j) ) - sum;
        }//End for-j
        //计算L矩阵的列
        for(i=k+1;i<row;i++)//控制L矩阵行的移动
        {//只需要计算对角线下面的元素,所以下标从k+1开始
            sum=0;
            for(j=0;j<k;j++)//U矩阵固定K列
                sum+= ( *(p+i*col+j) ) * ( *(p+j*col+k) );
            *(p+i*col+k)=( ( *(p+i*col+k) ) -sum)/( *(p+k*col+k) );
        }//End for-i
    }//End for-k

    for(i=0;i<row;i++) //通过Ly=b求Ux的结果y
    {//结果保存在增广矩阵最后一列
        sum=0;
        for(j=0;j<i;j++)
            sum+=( *(p+j*col+row) )*( *(p+i*col+j) );
        *(p+i*col+row)-=sum;
    }//End for-i

    for(i=row-1;i>=0;i--)//通过Ux=y求解x
    {
        for(j=row-1;j>i;j--)
            *(p+i*col+row)-=( *(p+j*col+row) ) * ( *(p+i*col+j) );
        ( *(p+i*col+row) )/=( *(p+i*col+i) );
    }//End for-i
    for(i=0;i<row;i++)
    {
        for(j=0;j<col;j++)
            printf("%lf ",*(p+i*col+j));
        printf("\n");
    }
}

克洛特分解法

//克洛特尔分解法,分解为单位上三角和下三角矩阵
void Crout(double *p,int row)
{
    int i,j,k;
    int col=row+1;
    double sum;

    for(k=0;k<row;k++)//先求L矩阵的列
    {
        for(i=k;i<row;i++)//i控制矩阵L行的移动
        {
            sum=0;
            for(j=0;j<k;j++)//j控制L矩阵列的移动,需要累乘至对角线前一个
                sum+= ( *(p+i*col+j) )*( *(p+j*col+k) );
            *(p+i*col+k)= ( *(p+i*col+k)-sum );//求L的k列i行
        }//End for-i

        for(j=k+1;j<row;j++)//求U矩阵的第k行
        {//j控制矩阵U列的移动
            sum=0;
            for(i=0;i<k;i++)//i控制U矩阵行的移动
                sum+=( *(p+k*col+i) )*( *(p+i*col+j) );
            *(p+k*col+j)=( *(p+k*col+j)-sum )/( *(p+k*col+k) );
        }//求U的k行j列
    }//End for-k

    for(i=0;i<row;i++)
    {//通过Ly=b求解结果y
        sum=0;
        for(j=0;j<i;j++)
            sum+=( *(p+i*row+j) )*( *(p+j*col+row) );
        *(p+i*col+row)=( (*(p+i*col+row))-sum )/( *(p+i*row+i) );
    }//End for-i

    for(i=row-1;i>=0;i--)//通过Ux=y求解x
        for(j=row-1;j>i;j--)
            *(p+i*col+row)-=( *(p+j*col+row) )*( *(p+i*col+j) );

    for(i=0;i<row;i++)
    {
        for(j=0;j<col;j++)
            printf("%lf ",*(p+i*col+j));
        printf("\n");
    }
}

乔里斯基分解法

//乔里斯基分解,分解为两个互为转置矩阵的矩阵
void Cholesky(double *p,int row)
{
    int i,j,k;
    int col=row+1;
    double sum;

    for(k=0;k<row;k++)
    {
        for(j=k;j<row;j++)//先求U的行
        {
            sum=0;
            for(i=0;i<k;i++)
                sum+=( *(p+k*col+i)*( *(p+i*col+j) )  );
            if(j==k)//对角线部分需要开根号
                *(p+k*col+j)=sqrt( ( *(p+k*col+j) )-sum  );
            else
                *(p+k*col+j)=(( *(p+k*col+j))-sum)/( *(p+k*col+k) );
            *(p+j*col+k)=*(p+k*col+j);//L和U互为转置,关于对角线对称
        }//End for-j
    }//End for-k

    for(i=0;i<row;i++)
    {//通过Ly=b求解结果y
        for(j=0;j<i;j++)
            *(p+i*col+row)-=( *(p+i*col+j) )*( *(p+j*col+row) );
        *(p+i*col+row)/=( *(p+i*col+i) );
    }

    for(i=row-1;i>=0;i--)
    {//通过Ux=y求解结果x
        for(j=row-1;j>i;j--)
            *(p+i*col+row)-=( *(p+i*col+j) )*( *(p+j*col+row) );
        *(p+i*col+row)/=( *(p+i*col+i) );
    }

    for(i=0;i<row;i++)
    {
        for(j=0;j<col;j++)
            printf("%lf ",*(p+i*col+j));
        printf("\n");
    }
}

 

全部评论

相关推荐

霁华Tel:秋招结束了,好累。我自编了一篇对话,语言别人看不懂,我觉得有某种力量在控制我的身体,我明明觉得有些东西就在眼前,但身边的人却说啥也没有,有神秘人通过电视,手机等在暗暗的给我发信号,我有时候会突然觉得身体的某一部分不属于我了。面对不同的人或场合,我表现出不一样的自己,以至于都不知道自己到底是什么样子的人。我觉得我已经做的很好,不需要其他人的建议和批评,我有些时候难以控制的兴奋,但是呼吸都让人开心。
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务