C-Programm mit Gauß-Elimination, Methode und Partielle Pivotierung zu lösen, Systeme von linearen algebraischen Gleichungen
Ich habe ein C-Programm (unten) nutzt die Gauss-Elimination-Methode und Partielle Pivotierung zu lösen, Systeme von linearen algebraischen Gleichungen. Mein Freund sagte mir, ich sollte ihn neu schreiben. Er erzählte mir zu Beginn Zyklen für mit 0 statt 1. Leider bin ich nicht in Kontakt mehr mit ihm, also er kann mir das nicht erklären, warum seine Lösung besser. Ich habe versucht zu umschreiben, die diese Zyklen für aber das Programm funktioniert nicht richtig. Vielleicht ist es neu schreiben müssen nur einige dieser Zyklen nicht alle von Ihnen. Würden Sie bitte erklären, das problem für mich (ich will dieses Programm, um perfekt zu sein)?
Systeme von linearen algebraischen Gleichung sieht aus wie A*X=B. Dieses Programm liest die Eingabe aus einer Datei 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);
}
Obwohl es wahrscheinlich nicht, warum dein code funktioniert der "trick" von Dekrementieren einen Zeiger auf
malloc()
ed storage und dann auf den ersten Wert mit Zeiger[1] hat Undefiniertes Verhalten unter C-standard. Es wird die meiste Zeit funktionieren, aber du wirst nie sicher wissen.HINWEIS:: Die Indizierung in vielen Beispielen in Numerische Rezepte in C ist nur Falsch und ruft Undefiniertes Verhalten indem Sie versuchen, auf die zugegriffen von Werten außerhalb der angegebenen Grenzen durch die Indizierung, z.B.
for (i = 1; i <= n; i++) {...}
. Die algorithmen sind in der Regel korrekt, die Indizierung ist einfach Murks durch eine Ungenauigkeit bei der übersetzung von FORTRAN nach C. (z.B. real-Zahl-arrays beginnen bei 1
in FORTRAN standardmäßig, und 0
immer in C)
InformationsquelleAutor Anakin | 2014-01-28
Du musst angemeldet sein, um einen Kommentar abzugeben.
Arrays in C sind natürlich indiziert, beginnend bei 0, nicht 1. Also, wenn Sie ein[5], erhalten Sie fünf Ablagefächer: ein[0], a[1], [2], [3], [4]. Also das "erste" element von array a a[0].
Du hast, um dieses mit einem trick. In Ihren Funktionen zuordnen, Sie dekrementiert den Zeiger zurückgegeben wird, die von malloc. Dies hat den Effekt der Verschiebung des array-slots über. Also, was ein[1] bezeichnet haben, ist jetzt immer was in a[0]. Das ermöglicht Ihnen die Verwendung der for-Schleifen ab 1 bis zu n.
Während der bestehende code wird wahrscheinlich gut funktionieren auf jedem normalen system und compiler, es ist nicht standard C. Das ist wahrscheinlich das, was dein Freund meinte den code umschreiben.
Wenn Sie gehen, um es zu ändern, entfernen Sie die Zeiger dekrementiert in der allocate-Funktionen, dann
ändern Sie die for-Schleifen gehen von 0 bis n-1 statt von 1 bis n.
Den for-Schleifen, die nicht, die gehen von 1 bis n, die Sie haben zu prüfen, die Logik sorgfältig, um zu sehen, ob es richtig ist. Denken Sie daran, niedrigsten array-element ist 0, die größte ist n-1.
InformationsquelleAutor Chris J. Kiick
Kann ich sehen, dass Ihr code ähneln viel Stil eingeführt (oder vorgestellt - ich weiß nicht, ob Sie ausgeliehen haben) in "Numerical Recipes in C". Ich Liebe dieses Buch, ich besitze das 1. (gelbe) Ausgabe, aber ich habe eine starke Kritik an die Adresse in Bezug auf Ihre Idee, dass die C-syntax ähneln Fortran. Die zwei Sprachen sind unterschiedlich und einfach Fortran (vor allem aus Fortran90) hat eine starke dynamische matrix-orientierter, während C als ein Licht, statische, mehrdimensionale syntax.
Insbesondere die zwei ungerade Punkte, die in Ihrem code, wie in Numerische Rezepte sind:
Ich persönlich denke, dass das problem noch übertroffen wurde mit C++ (siehe MTL, matrix template library zum Beispiel), mit denen Sie sich präsentieren können eine fast beliebige Schnittstelle mit einem x-beliebigen effiziente Umsetzung.
Allerdings glaube ich, dass es falsch suchen eine "schöne" syntax, wenn es nicht eine akzeptierte Sprache best practice (Punkt 1) und vor allem, wenn es nicht die Leistung wise (Punkt 2): die Verwendung von arrays von arrays, bedeutet das, dass Sie den Zugriff auf Elemente per Dereferenzierung zwei mal, jedes mal, wenn Sie den Zugriff auf ein element der matrix!
IMHO, kommen wir zurück zu C, Der richtige Weg - und ich muss oft angepasst NR-algorithmen an dieser Idee ist, dass ein 2D -, M (Zeilen) N (Spalten) Arrays sind dynamisch allokiert sein als:
Zugegriffen werden sowohl von Zeilen (Elemente hintereinander im Speicher zusammenhängend sind, und nach dem letzten element einer Zeile ist das erste element der folgenden Zeile), zählen von null:
Das ist, wie es ist häufiger für die C-Stil: dies ist, wie statische mehrdimensionale arrays in C funktioniert, ausser für die syntax zum dereferenzieren der Elemente (Hinweis: eine Dereferenzierung pro element).
Jedoch ist es möglich, das gleiche array "Spalten" (das ist die typische Fortran-memory-layout):
Beachten Sie, dass ich tauschten die zwei Schleifen: für die Leistung ist es besser - wenn möglich - zu erfüllen, die innere Schleife über die zusammenhängenden Elemente (mehr cache-freundlich).
Einer mehr praktischen und sehr wichtiger Grund für die Verwendung dieses Stils ist es, optimierte BLAS-und LAPACK-Bibliotheken (siehe ATLAS, libgoto, mkl, acml....) für C verwenden Sie diese beiden Ansätze (von Zeilen und Spalten), beginnend mit 0, die ganze Zeit.
Lassen Sie Ihren code perfekt sein, ich würde die NR - Stil zu einer mehr C-konformen Stil... ich hoffe, das ist der Sinn der Frage, und von Ihrem Freund ' s Kommentar 🙂
InformationsquelleAutor Sigismondo