我有一个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);
}
答案 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中所示:
我个人认为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库(请参阅ATLAS,libgoto,mkl,acml .... )对于C,使用这两种方法(按行和按列),始终从0开始计数。
为了让你的代码更完美,我会将NR的风格改为更符合C的风格......我希望这是问题的意义,以及你朋友的评论:)