C程序使用高斯消元法和部分旋转来求解线性代数方程组

时间:2014-01-28 13:59:42

标签: c

我有一个C程序(下面),它使用高斯消元法和部分透视法来求解线性代数方程组。我的朋友告诉我,我应该重写它。他告诉我以0而不是1开始 循环。不幸的是我不再与他联系,所以他无法向我解释为什么他的解决方案更好。我试图重写这些周期 for ,但程序无法正常工作。也许需要重写其中一些周期而不是全部周期。请你解释一下这个问题(我希望这个程序是完美的)?

线性代数方程组看起来像A * X = B.该程序从文件matrix.txt。

中读取输入
#include<stdio.h>
#include<stdlib.h>
#include<math.h>

typedef double **Matrix;
typedef double *Row;
typedef double *Col;
typedef double Elem;

Matrix allocate_matrix(int n);
Col allocate_col(int n);
Row allocate_row(int n);
void free_matrix(Matrix M, int n);

void pivot_partial(Matrix A, Col S,Col B, int n);
void forward_elimination(Matrix A,Col B,int n);
Col back_substitution(Matrix A, Col B, int n);
Col scale_factor(Matrix A,int n);
void gauss(Matrix A, Col B, int n);

void swap_rows(Row *r1, Row*r2);
void print_matrix(Matrix M, int n, char * name);
void print_col(Col C, int n, char *name);
void print_row(Row R, int n, char *name);

int main(int argc, char *argv[])
{
 FILE *ifp;
 int n,i,j;
 Matrix A;
 Col B;
 if(argc < 2)
 {
  printf("\nInput filename not passed \n");
  exit(1);
 }

 ifp = fopen(argv[1],"r");

 if(ifp == NULL)
 {
  printf("\nCould not open file %s\n",argv[1]);
  exit(1);
 }
 fscanf(ifp,"%i",&n);
  printf("A * X = B\n");
 printf("\nDimension(A) = %i\n",n);

 A = allocate_matrix(n);
 for( i = 1; i <= n; ++i)
  for(j = 1; j <= n; ++j)
   fscanf(ifp,"%lf", &A[i][j]);

 B = allocate_col(n);

 for(j = 1; j <= n; ++j)
  fscanf(ifp,"%lf",&B[j]);
 fclose(ifp);


 print_matrix(A,n,"A");
 print_col(B,n,"B");

 gauss(A,B,n);

 free_matrix(A,n);
 free(B + 1);

 getchar();
 return 0;
}

void print_matrix(Matrix M, int n, char * name)
{
 int i,j;
 printf("\n[%s] = ",name);
 printf("\n\n");
 for(i = 1; i <= n; i++)
 {
  for(j = 1; j <= n; ++j)
   printf("%6lG ",M[i][j]);
  printf("\n");
 }
}

void print_col(Col C, int n, char * name)
{
 int j;
 printf("\n[%s] = ",name);
 printf("\n\n");
 for(j = 1; j <= n; ++j)
  printf("%6lg\n",C[j]);

}

void print_row(Row R, int n, char * name)
{
 int i;
 printf("\n[%s] = ",name);
 for(i = 1; i <= n; ++i)
  printf("%6lg ",R[i]);
 printf("\n");
}

Matrix allocate_matrix(int n)
{
 Matrix A;
 int i,j;
 A = malloc(n * sizeof(Row));
 if(!A)
 {
  printf("\nError : Could not allocate 
                       memory for matrix\n");
  exit(1);
 }
 --A;

 for(i = 1; i <= n; ++i)
 {
  A[i] = malloc(n * sizeof(Elem));
  if(!A[i])
  {
   printf("\nError : Could not allocate 
                               memory for matrix\n");
   exit(1);
  }
  --A[i];
 }
 return A;
}

void free_matrix(Matrix M, int n)
{
 int i;
 for(i = 1; i <= n; ++i)
   free(M[i] + 1);
 free(M + 1);
}

Col allocate_col(int n)
{
 Col B;

 B = malloc(n * sizeof(Elem));

 if(!B)
 {
  printf("\nError : could not allocate 
                       memory\n");
  exit(1);
 }
 --B;
 return B;
}

Row allocate_row(int n)
{
 Row B;
 B = malloc(n * sizeof(Elem));
 if(!B)
 {
  printf("\nError : could not allocate
                       memory\n");
  exit(1);
 }
 --B;
 return B;
}

Col scale_factor(Matrix A, int n)
{
 int i,j;
 Col S ;
 S = allocate_col(n);

 for(i = 1; i <= n; ++i)
 {
  S[i] = A[i][1];
  for(j = 2; j <= n; ++j)
  {
   if(S[i] < fabs(A[i][j]))
       S[i] = fabs(A[i][j]);
  }

 }
 return S;

}

void pivot_partial(Matrix A, Col S,Col B, int n)
{
 int i,j;
 Elem temp;
 for(j = 1; j <= n; ++j)
 {
  for(i = j + 1; i <= n; ++i)
  {
   if(S[i] == 0)
   {
    if(B[i] == 0)
              printf("\nSystem doesnt
                                   have a unique solution");
    else
       printf("\nSystem is 
                                        inconsistent");
    exit(1);
   }
   if(fabs(A[i][j]/S[i])>fabs(A[j][j]/S[j]))
   {
    swap_rows(&A[i],&A[j]);
    temp = B[i];
    B[i] = B[j];
    B[j] = temp;
   }
  }

  if(A[j][j] == 0)
  {
   printf("\nSingular System Detected\n");
   exit(1);
  }
 }

}

void swap_rows(Row *r1, Row*r2)
{
 Row temp;
 temp = *r1;
 *r1 = *r2;
 *r2 = temp;
}

void forward_elimination(Matrix A,Col B,int n)
{
 int i,j,k;
 double m;

 for(k = 1; k <= n-1; ++k)
 {
  for(i = k + 1; i <= n; ++i)
  {
   m =  A[i][k] / A[k][k];
   for(j = k + 1; j <= n; ++j)
   {
    A[i][j] -= m * A[k][j];
    if(i == j && A[i][j] == 0)
    {
         printf("\nSingular 
                                        system detected");
         exit(1);
    }
   }
   B[i] -= m * B[k];
  }
 }
}

Col back_substitution(Matrix A, Col B, int n)
{
 int i,j;
 Elem sum;
 Col X = allocate_col(n);
 X[n] = B[n]/A[n][n];
 for(i = n - 1; i >= 1; --i)
 {
  sum = 0;
  for(j = i + 1; j <= n; ++j)
   sum += A[i][j] * X[j];
  X[i] = (B[i] - sum) / A[i][i];
 }
 return X;
}

void gauss(Matrix A, Col B, int n)
{
 int i,j;
 Col S, X;
 S = scale_factor(A,n);
 pivot_partial(A,S,B,n);
 forward_elimination(A,B,n);
 X = back_substitution(A,B,n);
 print_col(X,n,"X");

 free(S + 1);
 free(X + 1);
}

2 个答案:

答案 0 :(得分:1)

C中的数组自然从0开始索引,而不是1.所以如果你有[5],你会得到5个时隙:a [0],a [1],a [2],a [3],一个[4]。所以数组a的“第一”元素在[0]中。

你通过一个技巧解决了这个问题。在您的分配函数中,您递减从malloc返回的指针。这具有将阵列槽移位一的效果。那么[1]所提到的现在正在得到[0]中的内容。这允许你使用for循环开始1并上升到n。

虽然现有代码可能在任何普通系统和编译器上都能正常工作,但它不是标准C.这可能是你的朋友重写代码的意思。 如果要更改它,请删除分配函数中的指针减量 将for循环更改为0到n-1而不是1到n。

for (i=0; i < n; i++)

不会从1变为n的for循环,您必须仔细检查逻辑以确定它是否正确。记住,最低的数组元素是0,最大的是n-1。

答案 1 :(得分:1)

我可以看到你的代码类似于"Numerical Recipes in C"中介绍的风格(或呈现 - 我不知道他们是否已借用它)。我真的很喜欢这本书,我拥有第一版(黄色版),但我有一个强烈的批评来解决他们的想法,让C语法类似于Fortran的。这两种语言是不同的,简单的Fortran(尤其是Fortran90)具有强大的动态矩阵导向语法,而C语言则是轻量级,静态,多维语法。

特别是代码中的两个奇数点,如Numerical Recipes中所示:

  1. minor:正如Gene已经指出的那样,“技巧”将数组偏移,以便能够从1计数到N.
  2. 主要恐怖:将矩阵分配为向量数组的想法,以便能够将语法A [i] [j]与动态多维数组一起使用。
  3. 我个人认为C ++已经超越了这个问题(例如参见MTL,矩阵模板库),使用它可以提供几乎任意的接口和任意有效的实现。

    但是我认为,当它不是一种可接受的语言最佳实践(第1点)时,尤其是当它不是表现明智时,我认为搜索“漂亮”语法错误(第2点) ):使用数组数组意味着每次访问矩阵的一个元素时,通过解除引用两次访问元素!

    恕我直言,回到C,正确的方法 - 我经常根据这个想法调整NR的算法 - 是必须动态分配2D,M(行)到N(列)数组:

    float *A = (float*)malloc(M*N*sizeof(float));
    

    并且可以通过行访问(行中的元素在内存中是连续的,在行的最后一个元素之后,存在下一行的第一个元素),从开始计算:

    for(i=0; i<M; ++i) {
        for(j=0; j<N; ++j) {
            x = A[i*N + j];
            ...
        }
    }
    

    这就是C风格更常见的方式:这是C中静态多维数组的工作原理,除了用于取消引用元素的语法(注意:每个元素取消引用一个)。

    但是可以“按列”使用相同的数组(这是典型的Fortran内存布局):

    for(j=0; j<N; ++j) {
        for(i=0; i<M; ++i) {
            x = A[i + j*M];
            ...
        }
    }
    

    请注意,我已经交换了两个循环:为了性能,最好 - 尽可能 - 在连续元素上执行内部循环(更多缓存友好)。

    使用此样式的另一个实际且非常重要的原因是优化的BLAS和LAPACK库(请参阅ATLASlibgotomklacml .... )对于C,使用这两种方法(按行和按列),始终从0开始计数。

    为了让你的代码更完美,我会将NR的风格改为更符合C的风格......我希望这是问题的意义,以及你朋友的评论:)