1 Dev-C++ - TU Ilmenau
Transcription
1 Dev-C++ - TU Ilmenau
TU Ilmenau, Institut für Mathematik FG Numerische Mathematik und Informationsverarbeitung PD Dr. rer. nat. habil. W.Neundorf Datei: DEV CPP.TEX Hinweise zur Nutzung der Programmieroberfläche Dev-C++, der Formelsammlung zur Numerischen Mathematik in ANSI C und C++ ( Aachener Bibliothek“) sowie zur Erstellung von Program” men und Projekten in C++ und C 1 Dev-C++ Version Dev-C++ 4.9.6.0 Die Oberfläche mit den GNU-Compilern g++ und gcc lässt ein bequemes Arbeiten mit der Entwicklungsumgebung (Windows IDE) zu. Alle Einstellungen können von dort aus erfolgen. Die Arbeit soll an einfachen Beispielen erläutert werden. Nach diesem Muster können andere Programme gleichfalls erstellt und abgearbeitet werden. 1.1 Aufgabe 1 Demoprogramm a1c.cpp im Verzeichnis C:\d\Neundorf\nwptexte\tech phy\05\cpp gpp gcc // a1c.cpp // Rechnen mit komplexen Zahlen - Newton-Verfahren fuer Polynome #include #include #include #include #include <iostream.h> <stdio.h> <stdlib.h> <math.h> <conio.h> // // // // // deklariert Objekte wie cin, cout,... kann entfallen kann entfallen kann entfallen deklariert Objekte wie getch, printf, scanf, ... struct komplex { double re,im; }; // Addition: z = z1+z2 komplex add(komplex z1,komplex z2) { komplex z; z.re = z1.re+z2.re; z.im = z1.im+z2.im; return z; } // Subtraktion: z = z1-z2 komplex sub(komplex z1,komplex z2) { komplex z; z.re = z1.re-z2.re; z.im = z1.im-z2.im; return z; } // Betrag |z| double kabs(komplex z) { return sqrt(z.re*z.re+z.im*z.im); } // Multiplikation: z = z1*z2 komplex mult(komplex z1,komplex z2) { komplex z; z.re = z1.re*z2.re-z1.im*z2.im; z.im = z1.re*z2.im+z1.im*z2.re; return z; } // Division: z = z1/z2 komplex div(komplex z1,komplex z2) { komplex z,h; double nenner; h.re = z2.re; h.im = -z2.im; nenner = z2.re*z2.re+z2.im*z2.im; z.re = (z1.re*h.re-z1.im*h.im)/nenner; 1 z.im = (z1.re*h.im+z1.im*h.re)/nenner; return z; } int main() { komplex c1,c2,h,p,ps; komplex a[11]; char vz; int n,i; cout<<"Rechnen mit komplexen Zahlen - Newton-Verfahren\n"; cout<<"pn(x)=a[n]x^n+a[n-1]x^(n-1)+...+a[1]x+a[0] = 0\n"; cout<<"Polynomgrad n (n<=10): "; cin>>n; cout<<"\nEingabe Polynomkoeffizienten:\n"; for(i=0;i<=n;i++) { cout<<"\na["<<i<<"].re: "; cin>>a[i].re; cout<<"a["<<i<<"].im: "; cin>>a[i].im; } cout<<"\nStartwert Newton: x[0].re= "; cin>>c1.re; cout<<"Startwert Newton: x[0].im= "; cin>>c1.im; // Newton-Verfahren, Nullstellenberechnung fuer komplexe Polynome // unter Nutzung des zweizeiligen Hornerschema do { c2 = c1; p = a[n]; ps = p; for(i=n-1;i>0;i--) { h = mult(p,c2); p = add(h,a[i]); // p =p*x0+a[i]; h = mult(ps,c2); ps = add(h,p); // ps=ps*x0+p; } h = mult(p,c2); p = add(h,a[0]); // p=p*x0+a[0]; c1 = sub(c2,div(p,ps)); } while(kabs(sub(c1,c2))>1e-10); vz = (c1.im<0?’-’:’+’); cout.precision(16); cout<<"\nNullstelle c1: "<<c1.re<<vz<<fabs(c1.im)<<"*I\n"; getch(); return 0; } Die dortigen Include-Anweisungen betreffen Standard-Header-Dateien. Bei der Rechnung zahlreicher Beispiele und Ausgabe vieler Daten sind im Quelltext sogenannte Haltepunkte einzufügen. Das können solche Anweisungen sein wie getch(), dazu noch ev. das Löschen des BS mittels clrscr(). Dabei ist im Demoprogramm noch die Include-Anweisung für die Header-Datei conio.h einzutragen, also #include <conio.h> /*Direkte MSDOS Console I/O*/ Zur Ausführung sind die folgenden Schritte zu machen. 1. Menüpunkt Datei ⇒ Projekt oder Datei öffnen... ⇒ Datei öffnen Auswahlfenster: Suchen in: Dateiname: Dateityp: Schalter Öffnen Programm a1c.cpp erscheint in der Anzeigeleiste. 2. Menüpunkt Ausführen ⇒ Kompilieren 2 Fuehrt g++.exe... aus g++.exe "C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\a1c.cpp" -o "C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\a1c.exe" -s -I"C:\Dev-Cpp\include" -I"C:\Dev-Cpp\include\g++-3" -I"C:\Dev-Cpp\include" -L"C:\Dev-Cpp\lib" Ausfuehrung beendet Kompilierung erfolgreich Im Verzeichnis erscheint die ausführbare Datei a1c.exe. 3. Menüpunkt Ausführen ⇒ Ausführen Es öffnet sich das Eingabe- und Ergebnisfenster 2 C:\d\Neundorf\nwptexte\tech phy\05\cpp gpp gcc\a1c.exe Rechnen mit komplexen Zahlen - Newton-Verfahren pn(x)=a[n]xˆn+a[n-1]xˆ(n-1)+...+a[1]x+a[0] = 0 Polynomgrad n (n<=10): 2 Eingabe Polynomkoeffizienten: ... 4. Menüpunkt Ausführen ⇒ Kompilieren u. Ausführen Dasselbe Programm stellen wir als a1c .c bereit und übersetzen es. Dazu wird er Compiler gcc.exe aufgerufen. Als erstes kommt eine Meldung zur Header-Datei iostream.h, die in C nicht vorgesehen ist, weil es die Objekte cin, cout,... nicht gibt. 4 C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\a1c_.c iostream.h: No such file or directory. Wir kommentieren die entsprechende Zeile heraus und machen den nächsten Versuch, jedoch bei zahlreichen weiteren Fehlermeldungen. 14 14 15 C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\a1c_.c C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\a1c_.c C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\a1c_.c C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\a1c_.c 15 16 16 16 C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\a1c_.c C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\a1c_.c C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\a1c_.c C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\a1c_.c 21 21 C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\a1c_.c C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\a1c_.c C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\a1c_.c C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\a1c_.c C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\a1c_.c C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\a1c_.c C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\a1c_.c C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\a1c_.c 22 22 23 23 23 28 29 32 32 C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\a1c_.c C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\a1c_.c C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\a1c_.c C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\a1c_.c C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\a1c_.c C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\a1c_.c parse error before ‘add’ parse error before ‘z1’ [Warning] In function ‘add’: ‘komplex’ undeclared (first use in this function) [Build Error] (Each undeclared identifier is reported [Build Error] only once for each function it appears in.) parse error before ‘z’ ‘z’ undeclared (first use in this function) ‘z1’ undeclared (first use in this function) ‘z2’ undeclared (first use in this function) [Build Error] At top level: parse error before ‘sub’ parse error before ‘z1’ [Warning] In function ‘sub’: ‘komplex’ undeclared (first use in this function) parse error before ‘z’ ‘z’ undeclared (first use in this function) ‘z1’ undeclared (first use in this function) ‘z2’ undeclared (first use in this function) [Build Error] At top level: parse error before ‘z’ [Warning] In function ‘kabs’: ‘z’ undeclared (first use in this function) [Build Error] At top level: parse error before ‘mult’ parse error before ‘z1’ [Warning] In function ‘mult’: 3 33 33 34 34 34 C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\a1c_.c C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\a1c_.c C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\a1c_.c C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\a1c_.c C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\a1c_.c 39 39 40 350 C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\a1c_.c C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\a1c_.c C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\a1c_.c C:\Dev-Cpp\include\stdlib.h C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\a1c_.c C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\a1c_.c C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\a1c_.c C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\a1c_.c C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\a1c_.c C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\a1c_.c C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\a1c_.c C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\a1c_.c C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\a1c_.c C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\a1c_.c C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\a1c_.c C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\a1c_.c C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\a1c_.c C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\a1c_.c C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\a1c_.c C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\a1c_.c C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\a1c_.c C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\a1c_.c 40 40 41 41 43 43 48 48 53 55 60 66 73 74 74 76 ‘komplex’ undeclared (first use in this function) parse error before ‘z’ ‘z’ undeclared (first use in this function) ‘z1’ undeclared (first use in this function) ‘z2’ undeclared (first use in this function) [Build Error] At top level: parse error before ‘div’ parse error before ‘z1’ conflicting types for ‘div’ previous declaration of ‘div’ [Warning] In function ‘div’: ‘komplex’ undeclared (first use in this function) parse error before ‘z’ ‘h’ undeclared (first use in this function) ‘z2’ undeclared (first use in this function) ‘z’ undeclared (first use in this function) ‘z1’ undeclared (first use in this function) [Warning] In function ‘main’: ‘komplex’ undeclared (first use in this function) parse error before ‘c1’ ‘cout’ undeclared (first use in this function) ‘cin’ undeclared (first use in this function) ‘a’ undeclared (first use in this function) ‘c1’ undeclared (first use in this function) ‘c2’ undeclared (first use in this function) ‘p’ undeclared (first use in this function) ‘ps’ undeclared (first use in this function) ‘h’ undeclared (first use in this function) Man erkennt, dass eine Reihe von Veränderungen bzw. Korrekturen notwendig ist, um ein lauffähiges C-Programm zu erhalten. (1) In C++ reicht als Typangabe für eine Strukturvariable der Typbezeichner (hier komplex), in C ist der Typ durch struct komplex zu notieren. Dadurch kommen die meisten Fehlermeldungen. (2) Die Ein-/Ausgabeanweisungen cin, cout sind nicht deklariert und in C durch die Kommandos scanf, printf zu ersetzen. scanf erwartet als Argumente Adressen, so dass man den Adressoperator & direkt vor den Variablennamen stellt. (3) div ist in der Bibliothek stdlib.h als Standardfunktion vordefiniert und steht im Konflikt mit der gleichnamigen Nutzerfunktion. Diese sollte man anders bezeichnen (hier divi). (4) Die meisten Header-Dateien werden im Übersetzungsprozess durch Verlinken mit den Bibliotheken Dev-Cpp\include und Dev-Cpp\lib schon eingebunden. Damit ist auch die Funktion getch() verfügbar. Nach den Korrekturen erhält man das C-Programm a1c.c. // a1c.c // Rechnen mit komplexen Zahlen - Newton-Verfahren fuer Polynome //#include <stdio.h> //#include <stdlib.h> #include <math.h> // muss bleiben //#include <conio.h> struct komplex { double re,im; }; // Addition: z = z1+z2 struct komplex add(struct komplex z1,struct komplex z2) { struct komplex z; z.re = z1.re+z2.re; z.im = z1.im+z2.im; return z; } // Subtraktion: z = z1-z2 struct komplex sub(struct komplex z1,struct komplex z2) { struct komplex z; z.re = z1.re-z2.re; z.im = z1.im-z2.im; return z; } 4 // Betrag |z| double kabs(struct komplex z) { return sqrt(z.re*z.re+z.im*z.im); } // Multiplikation: z = z1*z2 struct komplex mult(struct komplex z1,struct komplex z2) { struct komplex z; z.re = z1.re*z2.re - z1.im*z2.im; z.im = z1.re*z2.im + z1.im*z2.re; return z; } // Division: z = z1/z2 struct komplex divi(struct komplex z1,struct komplex z2) { struct komplex z,h; double nenner; h.re=z2.re; h.im=-z2.im; nenner=z2.re*z2.re+z2.im*z2.im; z.re = (z1.re*h.re - z1.im*h.im)/nenner; z.im = (z1.re*h.im + z1.im*h.re)/nenner; return z; } int main() { struct komplex c1,c2,h,p,ps; struct komplex a[11]; char vz; int n,i; printf("Rechnen mit komplexen Zahlen - Newton-Verfahren\n"); printf("pn(x)=a[n]x^n+a[n-1]x^(n-1)+...+a[1]x+a[0] = 0\n"); printf("Polynomgrad n (n<=10): "); scanf("%i",&n); printf("\nEingabe Polynomkoeffizienten:\n"); for (i=0;i<=n;i++) { printf("\na[%i].re: ",i); scanf("%lf",&a[i].re); printf("a[%i].im: ",i); scanf("%lf",&a[i].im); } printf("\nNewton-Verfahren"); printf("\nStartwert: x[0].re= "); scanf("%lf",&c1.re); printf(" x[0].im= "); scanf("%lf",&c1.im); // Newton-Verfahren, Nullstellenberechnung fuer komplexe Polynome unter Nutzung des zweizeiligen Hornerschema do { c2 = c1; p = a[n]; ps = p; for(i=n-1;i>0;i--) { h = mult(p,c2); p = add(h,a[i]); // p =p*x0+a[i]; h = mult(ps,c2); ps = add(h,p); // ps=ps*x0+p; } h = mult(p,c2); p = add(h,a[0]); // p=p*x0+a[0]; c1 = sub(c2,divi(p,ps)); } while(kabs(sub(c1,c2))>1e-10); vz = (c1.im<0?’-’:’+’); printf("\nNullstelle: %17.15lf%c%17.15lf*I\n",c1.re,vz,fabs(c1.im)); getch(); return 0; } Man übersetzt es (⇒ a1c.exe) und führt es aus. Fuehrt gcc.exe... aus gcc.exe "C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\a1c.c" -o "C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\a1c.exe" -s -I"C:\Dev-Cpp\include" -L"C:\Dev-Cpp\lib" Ausfuehrung beendet Kompilierung erfolgreich 5 1.2 Aufgabe 2 Demoprogramm a4c.cpp im Verzeichnis C:\d\Neundorf\nwptexte\tech phy\05\cpp gpp gcc // a4c.cpp // Numerische Integration - zusammengesetzte Trapezregel #include #include #include #include #include <iostream.h> <stdio.h> <stdlib.h> <math.h> <conio.h> // deklariert Objekte wie cin, cout, ... // deklariert Objekte wie getch, printf, scanf, ... // Integranden double f1(double x) { return sin(x*x);} double f2(double x) { return sin(x)/x; } double f3(double x) { return sqrt(8.0*x)-x*x/8; } double f4(double x) { return x-x*x*x; } double f5(double x) { return 2.0*exp(-x*x)/sqrt(3.141592653589793); } // Zusammengesetzte Trapezregel double Trapez(double f(double),double a,double b,int n) { int k; double I,x,h; x = a; h = (b-a)/n; I = 0.5*(f(a)+f(b)); for(k=1;k<n;k++) { x = x+h; I = I+f(x); } return h*I; } int main() { double a,b; int n; a = 0; b = 1; n = 10; cout<<"Integralberechnungen mit zusammengesetzter Trapezregel\n"; cout<<"\nIntegrand f5(x)=2 exp(-x*x)/sqrt(Pi)\n"; cout<<"n="<<n<<"\n"; cout.precision(16); for(b=1;b<4.1;b=b+1.0) { cout<<"Integralwert Tn(f5,0,"<<b<<") = "<<Trapez(f5,a,b,n)<<"\n"; } n = 20; cout<<"\nn="<<n<<"\n"; for(b=1;b<4.1;b=b+1) { cout<<"Integralwert Tn(f5,0,"<<b<<") = "<<Trapez(f5,a,b,n)<<"\n"; } cout<<"\nExakter Wert = Int(f5,0,unendlich) = 1\n"; getch(); return 0; } Alle Parameter für die Integrationsformel sind im Programm definiert, so dass keine Eingabe vorgesehen ist. Nach Übersetzen und Ausführen von a4c.exe öffnet sich das Ergebnisfenster 6 2 C:\d\Neundorf\nwptexte\tech phy\05\cpp gpp gcc\a4c.exe Integralberechnungen mit zusammengesetzter Trapezregel Tn(f,a,b) Integrand f5(x)=2 exp(-x∗x)/sqrt(Pi) n=10 Integralwert Tn(f5,0,1) = 0.842008717 Integralwert Tn(f5,0,2) = 0.995048543 Integralwert Tn(f5,0,3) = 0.999971913 Integralwert Tn(f5,0,4) = 0.999999973 n=20 Integralwert Integralwert Integralwert Integralwert Tn(f5,0,1) Tn(f5,0,2) Tn(f5,0,3) Tn(f5,0,4) = = = = 0.842527817 0.995253490 0.999976360 0.999999981 Exakter Wert = Int(f5,0,unendlich) = 1 Wiederrum sind einige Veränderungen bzw. Korrekturen notwendig, um ein analoges lauffähiges C-Programm zu erhalten. // a4c.c // Numerische Integration - zusammengesetzte Trapezregel //#include <stdio.h> //#include <stdlib.h> #include <math.h> //#include <conio.h> // Integranden double f1(double x) { return sin(x*x);} ... double f5(double x) { return 2.0*exp(-x*x)/sqrt(3.141592653589793); } // Zusammengesetzte Trapezregel double Trapez(double f(double),double a,double b,int n) { int k; double I,x,h; ... } int main() { double a,b; int n; a = 0; b = 1; n=10; printf("Integralberechnungen mit zusammengesetzter Trapezregel Tn(f,a,b)\n"); printf("\nIntegrand f5(x)=2 exp(-x*x)/sqrt(Pi)\n"); printf("n=%d\n",n); for(b=1;b<4.1;b=b+1.0) { printf("Integralwert Tn(f5,0,%1.0lf)=%11.9lf\n",b,Trapez(f5,a,b,n)); } n = 20; printf("\nn=%d\n",n); for(b=1;b<4.1;b=b+1) { printf("Integralwert Tn(f5,0,%1.0lf)=%11.9lf\n",b,Trapez(f5,a,b,n)); } printf("\nExakter Wert = Int(f5,0,unendlich) = 1\n"); getch(); return 0; } 7 1.3 Aufgabe 3 Demoprogramm a5c.cpp im Verzeichnis C:\d\Neundorf\nwptexte\tech phy\05\cpp gpp gcc // a5c.cpp // Koerpergewicht #include #include #include #include <stdio.h> <stdlib.h> <math.h> <conio.h> /* deklariert Objekte wie getch, printf, scanf */ int main(void) { short groesse; float gewicht, ideal; char auswahl, ret; char ende; do { /* Gibt ’int’ zurueck */ /* Kontrollvariable */ /* Schleifenanfang */ // clrscr(); /* Diese Funktion loescht den Bildschirm! */ // /* Headerdatei: <conio.h> */ /* Achtung: clrscr() funktioniert nicht mit Visual C++, Dev-C++,... */ printf("\n\n\t Bitte geben Sie Ihre Koerpergroesse (in cm) ein: "); scanf("%d%c", &groesse, &ret); printf("\n\t Bitte geben Sie Ihr Gewicht (in kg) ein: "); scanf("%f%c", &gewicht, &ret); printf("\n\t Bitte geben Sie Ihr Geschlecht an:"); printf("\n\t m = Mann"); printf("\n\t f = Frau\n\t "); // !=m ist Frau auswahl = getch(); printf("%c\n", auswahl); /* Hier erfolgt nun der Aufruf unserer neuen Funktion: ideal = errechne_ideal(groesse, auswahl); */ printf("\n\t Ihr Idealgewicht waere: %5.1f kg", ideal); if(ideal<gewicht) printf("\n\n\t Sie haben %5.1f kg Uebergewicht!", gewicht - ideal); else printf("\n\n\t Sie haben %5.1f kg Untergewicht!", ideal - gewicht); printf("\n\n\t Moechten Sie das Programm beenden (j/n)?"); ende = getch(); }while(ende!=’j’); /* Abbruchbedingung */ /* Schleifenende */ printf("\n\n\t Taste druecken..."); getch(); return 0; /* Ein Programm das 0 zurueck gibt */ /* wurde ordnungsgemaess beendet */ } // Definition des Prototyps float errechne_ideal(int groesse, char geschlecht) { float ideal; ideal = groesse-100; if(geschlecht==’m’) ideal = ideal/100*90; else ideal = ideal/100*85; return ideal; /* gibt den Inhalt von ’ideal’ zurueck */ } Alles scheint in Ordnung zu sein, aber bei dem Versuch das Programm zu kompilieren weigert sich g++ den Quelltext zu übersetzen. [Warning] In function ‘int main()’: implicit declaration of function ‘int errechne_ideal(...)’ 8 Das liegt daran, dass die Funktion errechne_ideal() erst nach der Hauptfunktion definiert wurde. So mancher C-Compiler lässt sich überlisten, indem man die Funktion einfach vor der Hauptfunktion definiert. Die korrekte Lösung besteht jedoch in der Verwendung eines Prototypes, an dem der Compiler erkennen kann das irgendwo weiter unten im Quelltext noch eine entsprechende Funktion definiert ist. // Deklaration des Prototyps float errechne_ideal(int groesse, char geschlecht); Diesen fügt man als Zeile zwischen die #include-Anweisungen und die main-Funktion ein. Dies ist der übliche Ort an dem Prototypen platziert werden sollten. Da die Eingabe von Daten mit der <Enter>-Taste beendet wird, haben wir das <Enter>Zeichen extra einer Variablen zugeordnet. scanf("%d%c", &groesse, &ret); Damit sichert man, dass der Tastaturpuffer geleert wird. Man kann dies auch mit dem Aufruf der Methoden flush() bzw. fflush(stdin) mit dem Standardbezeichner stdin aus den Header-Files stdio.h oder conio.h tun. Oft passiert dies jedoch automatisch mit den Abschluss der Eingabe einfach durch die <Enter>-Taste und nach der Anweisung cout<<. Es sind keine Veränderungen bzw. Korrekturen notwendig, um daraus ein lauffähiges CProgramm zu erhalten. Man bezeichnet einfach die Datei um in a5c.c. 1.4 Aufgabe 4 Demoprogramm matsum.cpp im Verzeichnis C:\d\Neundorf\nwptexte\tech phy\05\cpp gpp gcc // matsum.cpp // Summe c = a+b #include #include #include #include #include zweier Matrizen der Dimension (m,n) <iostream.h> <stdio.h> <stdlib.h> <math.h> <conio.h> // deklariert Objekte wie cin, cout,... // deklariert Objekte wie getch, printf, scanf,... void summe(int m,int n,long double x[][3],long double y[][3],long double z[][3]); int main() { long double a[2][3] = {0.987654321e-12,-2,1,3,4,7}; long double b[2][3] = {2.0e-5,4,8,-5,-5,-5}; // // // // man teste und beobachte die Ausgabe long double b[2][3] = {2.0e-4,4,8,-5,-5,-5}; long double b[2][3] = {2.0e-3,4,8,-5,-5,-5}; ... long double c[2][3] = {1.0,1,1,1,1,1}; cout << "\nSumme c = a+b von Matrizen" ; cout << "\n============================" << endl; summe(2,3,a,b,c); cout.precision(16); cout << "c[0][0] = " cout << "c[0][1] = " cout << "c[0][2] = " cout << "c[1][0] = " cout << "c[1][1] = " cout << "c[1][2] = " getch(); return 0; // << << << << << << double ! c[0][0] << c[0][1] << c[0][2] << c[1][0] << c[1][1] << c[1][2] << endl; endl; endl; endl; endl; endl; } 9 // long double = extended void summe(int m, // Zeilenzahl int n, // Spaltenzahl long double x[][3], // erste Matrix long double y[][3], // zweite Matrix long double z[][3]) // Summenmatrix { for(int i=0;i<m;++i) for(int k=0;k<n;++k) z[i][k] = x[i][k]+y[i][k]; } Ausgewählte Ergebnisse Summe c = a+b von Matrizen ============================ c[0][0] = 2.000000098765432e-05 c[0][1] = 2 c[0][2] = 9 c[1][0] = -2 c[1][1] = -1 c[1][2] = 2 --------------------------------------------------------------------------------cout.precision(16); long double a[2][3] = {0.987654321e-12,-2,1,3,4,7}; long long long long long long long long long double double double double double double double double double b[2][3] b[2][3] b[2][3] b[2][3] b[2][3] b[2][3] b[2][3] b[2][3] b[2][3] = = = = = = = = = {2.0e-6,4,8,-5,-5,-5}; {-2.0e-5,4,8,-5,-5,-5}; {2.0e-5,4,8,-5,-5,-5}; {2.0e-4,4,8,-5,-5,-5}; {2.0e-3,4,8,-5,-5,-5}; {2.0e-2,4,8,-5,-5,-5}; {2.0e-1,4,8,-5,-5,-5}; {2.0e0,4,8,-5,-5,-5}; {2.0e1,4,8,-5,-5,-5}; => => => => => => => => => c[0][0] c[0][0] c[0][0] c[0][0] c[0][0] c[0][0] c[0][0] c[0][0] c[0][0] = = = = = = = = = 2.000000987654321e-06 -1.999999901234568e-05 2.000000098765432e-05 0.0002000000009876543 0.002000000000987654 0.02000000000098765 0.2000000000009877 2.000000000000988 20.00000000000099 Man bemerkt den Wechsel im Ausgabeformat bei 16 gültigen dezimalen Mantissenstellen. Sobald die auszugebene Dezimalzahl betragsmäßig kleiner als 10−4 ist, erfolgt ein Übergang zum Gleitpunktformat mit skalierter Mantisse. Um ein analoges lauffähiges C-Programm zu erhalten, sind einige Veränderungen bzw. Korrekturen notwendig. 1. Der Datentyp long double steht in C nicht zur Verfügung. Es wird zwar, wenn man damit arbeitet, kein syntaktischer Fehler angezeigt, aber bei Überprüfung der Initialisierung der Felder erkennt man ihre falsche Belegung. 2. In C kann die Schleifenvariable nicht direkt im Initialisierungsteil deklariert werden, dies muss im umliegenden Bereich erfolgen. // matsum.c // Summe c = a+b zweier Matrizen der Dimension (m,n) //#include <stdio.h> //#include <stdlib.h> #include <math.h> //#include <conio.h> 10 void summe(int m,int n, double x[][3], double y[][3], double z[][3]); // nicht in C: long double = extended int main() { double a[2][3] = {0.987654321e-12,-2,1,3,4,7}; double b[2][3] = {2.0e-5,4,8,-5,-5,-5}; double c[2][3] = {1.0,1,1,1,1,1}; // auch moeglich // double c[2][3] = {{1.0,1,1}, // {1,1,1}}; printf("\nSumme c = a+b von Matrizen"); printf("\n============================\n"); summe(2,3,a,b,c); printf("c[0][0] printf("c[0][1] printf("c[0][2] printf("c[1][0] printf("c[1][1] printf("c[1][2] getch(); return 0; = = = = = = %17.15e\n",c[0][0]); %.0lf\n",c[0][1]); %.0lf\n",c[0][2]); %.0lf\n",c[1][0]); %.0lf\n",c[1][1]); %.0lf\n",c[1][2]); } void summe(int int n, double double double { int i,k; m, x[][3], y[][3], z[][3]) // // // // // Zeilenzahl Spaltenzahl erste Matrix zweite Matrix Summenmatrix for(i=0;i<m;++i) // Deklaration von i,k nicht im Initialisierungsteil for(k=0;k<n;++k) z[i][k] = x[i][k]+y[i][k]; } Ergebnisse Summe c = a+b von Matrizen ============================ c[0][0] = 2.000000098765432e-005 c[0][1] = 2 c[0][2] = 9 c[1][0] = -2 c[1][1] = -1 c[1][2] = 2 11 1.5 Aufgabe 5 Demoprogramm tnewton1.c im Verzeichnis C:\d\Neundorf\nwptexte\tech phy\05\dev cpp Die Definitionen und Prototypen, also die Prozedurköpfe usw., findet man in den HeaderFiles basis.h, u_proto.h, tfunc1.h als Vorabdeklarationen und werden damit bekannt gemacht. Ihre eigentliche Definition erfolgt in den Funktionen fnewtonc.c, tfunc1.c, basis.c, die nach der Hauptfunktion main stehen. // tnewton1.c /*-------------------------------------------------------------------*/ /* Test program for newton */ /*-------------------------------------------------------------------*/ //#include <math.h> ... #include <conio.h> #include "basis.h" #include "u_proto.h" #include "tfunc1.h" in Header-File basis.h // wegen getch() int main (void) { int i, rc, iter; REAL x, f; REAL (*fct) (REAL), (*fctd) (REAL); char *text; WriteHead ("Newton Method for real, nonlinear functions"); for(i=1;i<=6;i++) { switch (i) { case 1: x = ONE; fct = f1; fctd = f1s; text = "f = 5*x-exp(x)"; break; case 2: x = TWO; fct = f2; fctd = f2s; text = "f = (((((x-6)*x+15)*x-20)*x+15)*x-6)*x+1"; break; case 3: x = ONE; fct = f3; fctd = f3s; text = "f = sin(x)"; break; case 4: x = ONE; fct = f4; fctd = f4s; text = "f = 1+sin(x)"; break; case 5: x = TWO; fct = f5; fctd = f5s; text = "f = exp(x)-(1.0+x+x*x*0.5)"; break; case 6: x = TWO; fct = f6; fctd = f6s; text = "f = (x-1.0)*(x-1.0)*(sin(PI*x)-log(fabs(2.0*x/(x+1.0)))"; break; default: return (1); } printf("%s\n", text); printf("Start value x0 = "); printf(FORMAT_LF, x); printf("\n"); rc = newton(fct,fctd,&x,&f,&iter); 12 printf("Return code = printf("Root = printf("\nFunction value printf("\nIterations % d\n", rc); "); printf (FORMAT_2016LF, x); = "); printf (FORMAT_LE, f); = % d\n\n", iter); } WriteEnd(); getch(); return (0); } // fnewton.c /* Loesung nichtlinearer Gleichungen Numerische Verfahren zur Loesung nichtlinearer Gleichungen Newton-Verfahren fueur einfache und mehrfache Nullstellen #include "basis.h" #include "u_proto.h" #define ITERMAX 300 #define ABSERR ZERO #define RELERR (REAL)((REAL)128.0 * MACH_EPS) #define FCTERR (REAL)((REAL)4.0 * MACH_EPS) int newton ( REALFCT REALFCT REAL * REAL * int * ) /* /* /* /* */ Maximale Iterationszahl */ Zugelassener Absolutfehler */ Zugelassener Relativfehler */ Max. Fehler im Funktionwert*/ /* Eindimensionales Newton Verfahren .........*/ fct, /* Funktion ........................*/ fderv, /* 1. Ableitung ....................*/ x, /* Startwert / Loesung .............*/ fval, /* Funktionswert an Loesung........ */ iter /* Iterationszahl ..................*/ /* Die Funktion newton realisiert das Newton-Iterationsverfahren * zur Loesung der Gleichung fct(x) = 0. * Die Funktion fct und deren 1. Ableitung muessen als Parameter * uebergeben werden. ... // tfunc1.c /* Definition of test functions for newton, pegasus and roots * * * */ */ #include "basis.h" #include "u_proto.h" #include "tfunc1.h" REAL f1 (REAL x) { return ((REAL)5.0*x-EXP(x)); REAL f1s (REAL x) { return ((REAL)5.0-EXP(x)); } REAL f1ss (REAL x) { return (-EXP(x)); } ... /* f(x) = 5*x -exp(x) */ } // basis.c /* grundlegende Funktionen: Definitionsdatei #include "basis.h" /* /* /* /* /* /* /* /* /* */ wegen NULL, freopen, stdout, fprintf, stderr, stdin, SQRT, EXP, sqrt, MACH_EPS, POSMAX, epsquad, maxroot, pi, ATAN, sqr, umleiten, readln, intervall, horner, norm_max, skalprod, copy_vector, REAL, ONE, TWO, FOUR, ZERO, HALF, FABS, boolean, FOUR, basis, mach_eps, epsroot, exp_1, posmin, sqrtlong, comdiv, comabs, quadsolv, SetVec, CopyVec, ReadVec, WriteVec, SetMat, CopyMat, ReadMat, WriteMat, WriteHead, WriteEnd, LogError, fgetc, stdin, SWAP */ */ */ */ */ */ */ */ */ ... Die Programme sind aus der Bibliothek FORMELN\CNUM8 (Formelsammlung zur Numerischen Mathematik in ANSI C und C++, Aachener Bibliothek) entnommen und zusammengestellt. Das Programm tnewton1.c wir mit dem Compiler gcc übersetzt (→ tnewton1.exe) und man führt es aus. 13 Das Ergebnisfenster ist 2 C:\d\Neundorf\nwptexte\tech phy\05\dev cpp\tnewton1.exe ——————————————————————————– Newton Method for real, nonlinear functions ——————————————————————————– f = 5∗x-exp(x) Start value x0 = 1.000000 Return code = 0 Root = 0.2591711018190737 Function value = -1.758576e-016 Iterations =5 f = (((((x-6)∗x+15)∗x-20)∗x+15)∗x-6)∗x+1 Start value x0 = 2.000000 Return code = 2 Root = 1.0059508814409825 Function value = 4.451994e-014 Iterations = 300 f = sin(x) Start value x0 = 1.000000 Return code = 0 Root = 0.0000000000000000 Function value = 0.000000e+000 Iterations =5 f = 1+sin(x) Start value x0 = 1.000000 Return code = 0 Root = -1.5707963497819439 Function value = 2.642201e-016 Iterations = 26 f = exp(x)-(1.0+x+x∗x∗0.5) Start value x0 = 2.000000 Return code = 0 Root = 0.0000148767867855 Function value = 4.037569e-016 Iterations = 162 f = (x-1.0)∗(x-1.0)∗(sin(PI∗x)-log(fabs(2.0∗x/(x+1.0))) Start value x0 = 2.000000 Return code = 0 Root = 2.0981196117437797 Function value = -2.541919e-016 Iterations =5 ——————————————————————————– 14 2 Aufbau der Numerik-Bibliothek In Abschnitt 1, Aufgabe 5, ist eine Programmbibliothek verwendet worden. Die Aachener Bibliothek ist hierarchisch aufgebaut, wohl strukturiert und nach Teildisziplinen der Numerischen Mathematik angelegt. 1. Die Programme befinden sich auf dem Novell-Netz RUNGE (Volume share) des IfMath Q:\Neundorf\stud m93\FORMELN\ANSICNUM, ...\CNUM8 Die neuere Version CNUM8 om 26.04.1996 enthält mehr und verbesserte numerische Algorithmen. Lokal ist das Bibliotheksverzeichnis z. B. unter c:\d\Neundorf\FORMELN\ANSICNUM, ...\CNUM8 2. Literaturhinweis - Engeln-Müllges, G., Reutter, F.: Numerik-Algorithmen mit ANSI-C-Programmen. Wissenschaftsverlag Mannheim 1993. Standort: 50/51=MAT 93 A 7745. - Engeln-Müllges, G., Reutter, F.: Formelsammlung zur Numerischen Mathematik mit FORTRAN 77-Programmen. Bibliogr. Institut Mannheim 1988 (auch für TP, C). 3. Informationen zum Aufbau der Bibliothek, ihrer Verzeichnisstruktur und zu ihrer Benutzung (z. B. Anwendung und Einstellungen für unterschiedliche Compiler, Benutzungsarten) findet man in der Datei LIESMICH.TXT im jeweiligen Verzeichnis. 4. Analog gibt es Verzeichnisse von Versionen der Numerik-Tools für andere Programmiersprachen, z. B. für Turbo Pascal im Verzeichnis \TPNUM mit ähnlicher Struktur. 5. Inhalte der einzelnen Unterverzeichnisse Verzeichnis Inhalt \CNUM8\02 \CNUM8\02\TST Quellen der Verfahren für nichtlineare Gleichungen Quellen der zugehörigen Testprogramme \CNUM8\03 \CNUM8\03\TST \CNUM8\03\EIN Quellen der Verfahren für Polynomnullstellen Quellen der zugehörigen Testprogramme Polynomkoeffizienten \CNUM8\04 \CNUM8\04\TST \CNUM8\04\EIN Quellen der Verfahren für LGS, EWP Quellen der zugehörigen Testprogramme Matrizen, Vektoren, EW, EV \CNUM8\05 .................. \CNUM8\17 \CNUM8\18 \CNUM8\18\TST \CNUM8\18\EIN \CNUM8\BASIS \CNUM8\INC \CNUM8\LIB \CNUM8\REST \CNUM8\MAKEFILE.MK \CNUM8\LIESMICH.TXT Quellen weiterer Verfahren Quellen der Verfahren für AWP von DGL und DGL-Syst. Quellen der Verfahren für RWP von DGL Quellen wichtiger Basisprogramme enthält sämtliche Header-Dateien Library i. Allg. nicht benötigt 15 3 Projekte in Borland-C 3.1 Borland-C++3.1 (DOS) Der Borland-C-Compiler lässt ein bequemes Arbeiten mit der Entwicklungsumgebung (DOS IDE) zu. Alle Einstellungen können von dort aus erfolgen. 1. Beispiel: Nullstellenbestimmung mit dem Newton-Verfahren Die mögliche Arbeit mit dem Compiler C++3.1 sol an einem ersten einfachen Beispiel erläutert werden. Nach diesem Muster können andere Programme gleichfalls erstellt und abgearbeitet werden. Aufgabe: das Demoprogramm TNEWTON.C im Verzeichnis \CNUM8\02\TST ist zu übersetzen und auszuführen. Dazu sind die folgenden Schritte empfehlenswert. (a) Man lege im Verzeichnis \CNUM8 ein Arbeitsverzeichnis für EXE- und OBJDateien an, im weiteren mit \EXE bezeichnet. (b) Man starte den Borland-Compiler, und wechsle in das Verzeichnis \CNUM8\02\TST. (c) Man lade das Demoprogramm TNEWTON.C. Die dortigen Include-Anweisungen für die Header-Dateien enthalten schon wichtige Hinweise dafür, welche Files zum Projekt gehören. #include <basis.h> #include <u_proto.h> #include <tfunc1.h> /*Grundlegender Deklarationsteil*/ /*Vordeklaration aller Bibliotheksfunktionen*/ /*Testfunktionen*/ (d) Öffnen eines Turbo C - Projektfiles TNEWTON.PRJ mittels Project, Open project. (e) Man füge mittels Project, Add item die folgenden benötigten Files in das Projekt ein. \CNUM8\02\TST\TNEWTON.C \CNUM8\02\FNEWTON.C \CNUM8\02\TST\TFUNC1.C \CNUM8\BASIS\BASIS.C Noch einmal der Hinweis: Erkenne ev. die benötigten Files aus den IncludeAnweisungen der Anwendungsbeispiele. (f) Man stelle mittels Options, Directories die zusätzlichen (!) Verzeichnisse ein. Include Directories Library Output Source : : : : ;..\CNUM8\INC keine Änderung oder ev. ;..\CNUM8\LIB ..\CNUM8\EXE keine Änderung oder ev. ..\CNUM8\02\TST Das standardmäßige Include-Verzeichnis ist meist ..\BC31\INCLUDE. Die Standardbibliotheken sind ..\BC31\LIB, ..\BC31\CLASSLIB\LIB, ..\BC31\OWL\LIB, ..\BC31\LIB\STARTUP. (g) Man übersetzt mittels Compiler, Build all das Demoprogramm TNEWTON.C, d.h. das Projekt TNEWTON.PRJ. Der Ablauf erfolgt in den Schritten Compiling und Linking. Die ausführbare Datei TNEWTON.EXE steht im Verzeichnis \CNUM8\EXE zur Verfügung. 16 (h) Bei erfolgreicher Übersetzung ist eine Kontrolle der in TNEWTON.C einbezogenen Header-Dateien möglich mittels Project, Include files. (i) Start des übersetzten Projekts gleich in der Entwicklungsumgebung mittels Run, Run. Die letzten Ergebnisse sind mittels Window, Output zu besichtigen. Bei der Rechnung zahlreicher Beispiele und Ausgabe vieler Daten über mehrere Bildschirme sind im Quelltext sogenannte Haltepunkte einzufügen. Das können solche Anweisungen sein wie getch(), dazu noch ev. das Löschen des BS mittels clrscr(). Dabei ist möglicherweise im Demoprogramm noch die Include-Anweisung für die Header-Datei conio.h einzutragen, also #include <conio.h> /*Direkte MSDOS Console I/O*/ (j) Es empfiehlt sich, vor Verlassen der Entwicklungsumgebung das Projekt zu speichern. Mittels Options, Save, Project wird die Projektdatei TNEWTON.PRJ in das Verzeichnis \CNUM8\02\TST gespeichert. Auch die gesetzten Directories werden gespeichert. Damit ist eine erneute Behandlung dieses Projekts schnell möglich. (k) Natürlich kann man die Datei TNEWTON.EXE aus der DOS- oder WindowsOberfläche starten. 2. Beispiel: Lösung einer Anfangswertaufgabe für Systeme von gewöhnlichen Differentialgleichungen mittels Runge-Kuttaund anderer Verfahren mit Schrittweitensteuerung Aufgabe: das Demoprogramm M AWP.C im Verzeichnis \CNUM8\17\TST ist zu übersetzen und auszuführen. Dazu sind ähnlich wie im obigen Beispiel die folgenden Schritte auszuführen. Wir beschränken uns auf die Angabe einiger Unterschiede. (a) Das Demoprogramm ist M AWP.C. (b) Öffnen eines Projektfiles M AWP.PRJ mittels Project, Open project. (c) Man füge mit Project, Add item die folgenden benötigten Files in das Projekt ein. \CNUM8\17\TST\M AWP.C \CNUM8\17\AWP.C \CNUM8\17\TST\T DGLS.C \CNUM8\BASIS\VMBLOCK.C \CNUM8\BASIS\BASIS.C (d) Es gibt noch ein Verzeichnis \CNUM8\17\EIN mit Eingabedateien der Parameter der verschiedenen Beispiele. (e) Normalerweise erfolgt eine Eingabe im Dialog. Gemäß der im Quelltext T DGLS.C vorliegenden Beispiele sind die Beispielnummer und danach die Verfahrensparameter nrbeisp , εa , εr , nrmeth , x0 , y1 (x0 ), y2 (x0 ), h, xend , fmax einzugeben. Die Eingabeaufforderung kann wahlweise angezeigt oder unterdrückt werden. Um diese anzuzeigen, ist im Demoprogramm die Anweisung #define INTERAKTIV noch zu ergänzen. Haltepunkte wie getch() sind bei der Ausgabe großer Datenmengen als auch nach Fehlermeldungen sinvoll. 17 Übersetzen mit dem Borland-C-Compiler C++3.1 (Windows) Unter Windows sollte man ein eigenständiges Projekt erstellen. Ein Zusammenspiel“ mit DOS ist nicht ganz problemlos. ” Dazu die Bemerkungen: 1. Es können Projekte, die unter DOS IDE erstellt wurden, auch in die Windows IDE laden. Dann erscheint ein Dialogfenster mit der Frage, ob die Optionen des DOS IDE umzukonfigurieren sind. 2. EXE-Dateien von Projekten unter Windows IDE nur unter Windows aufrufen. 3. Verwendet man ein Projekt aus Windows IDE in der Entwicklungsumgebung DOS, ist aufgrund von Voreinstellungen durch Windows die Übersetzung Compiler, Build all möglich, nicht aber die Ausführung Run, Run. 3.2 Borland-C++4.52, C++5.02 Die Compiler lassen ein bequemes Arbeiten mit der Entwicklungsumgebung (IDE) zu. Man bemerke jedoch eine Reihe von Unterschieden zur Oberfläche von C++3.1. Alle Einstellungen können von der IDE aus erfolgen, auch die Wahl von Windows-Oberfläche oder DOS-Standard. 1. Erstellen eines neuen Projekts. Die einzelnen Schritte seien grob umrissen. (a) File, New, Project, New Target (C++5.02) Projekt, Neues Projekt, Neues Ziel (C++4.52) Projektverzeichnis und -name, Zielname eintragen. Die Typkennung ist ide. Zur Illustration sei der gewählte Projektname ..\CNUM8\17\TST\proj0001.ide Weiterhin ist der Zieltyp anzugeben. Unter den zahlreichen Varianten sind hier zwei gebräuchliche genannt: - Anwendung[.exe] mit Umgebung DOS (Standard) - EasyWin[.exe] mit Umgebung Windows 3.x(16), Ausgabe in einem Windows-Fenster Es wird nun das Fenster · - proj0001[.exe] · |− 2 proj0001[.cpp] | 2 proj0001[.def] · |− · |− 2 proj0001[.rc] Projekt geöffnet mit den Standardvorgaben (LinkTarget) (CppCompile) (SourceInclude) (CompilerResources) Dort kann nun das Projekt konfiguriert werden, indem insbesondere überflüssige Knoten (Programme) gelöscht und neue Knoten hinzugefügt werden. Zwecks Zusammenstellung der Projektfiles (hierarchische Struktur) gibt es die - Arbeit mit der rechten Maustaste - oder Tastenbetätigung (Einf, Entf, ↑, ↓, ... ). Ein zusammengestelltes Beispielprojekt ist im Punkt 3 angegeben. Achtung! Beim Zieltyp Anwendung[.exe] in der DOS-Umgebung (Plattform=DOS(Standard)) sind die Einstellungen in den Standardbibliotheken zu kontrollieren. Insbesondere ist bei Nutzung von Graphik in C-Programmen explizit die Bibliothek BGI hinzuzufügen. 18 Vorgehensweise: - Im Projektfenster mittels rechter Maustaste auf proj0001[.exe] klicken. - TargetExpert auswählen. - Im TargetExpert-Fenster die Standardbibliothek BGI ergänzen. (b) Optionen, Projekt Im Fenster Projektoptionen sind Verzeichnisse zu aktualisieren. - Quelltextverzeichnisse Include : ;..\CNUM8\INC Bibliothek : keine Änderung oder ev. ;..\CNUM8\LIB Quelltext : ..\CNUM8\17\TST (c) (d) (e) (f) - Ausgabeverzeichnisse OBJ-Dateien : ..\CNUM8\EXE EXE-Datei : ..\CNUM8\EXE Optionen, Speichern Im Fenster Optionen speichern der Konfiguration des Projekts. Projekt, Projekt neu compilieren Hier erfolgt die Übersetzung des Gesamtprojekts. Im Fenster Compilerstatus wird der Ablauf dieses Prozesses angezeigt. Im Fenster Meldung erscheinen Informationen zur Übersetzung sowie zu ev. Warnungen und Fehlern. Debug, Ausführen (oder Strg F9 bzw. Blitz“-Ikone) ” Starten des Projekts. Der Eingabedialog sowie die Ergebnisdarstellung erfolgen in einem Windows-Fenster. Vor einem wiederholten Start des Projekts ist dieses i. Allg. neu zu öffnen (außer bei Fehlermeldugen). Nach Beendigung der Arbeit am Projekt können einige Dateien gelöscht werden, so z. B. im Verzeichnis \CNUM8\17\TST die Dateien - proj0001.dsw (Turbo C Context File) - proj0001.csm (Konfigurationsfile) - proj0001.˜de (Bak-Datei von proj0001.ide) - proj0001.obr (Browser State File) Achtung! Auch ein Programm, das nur Standard-Header-Dateien enthält, sollte als Projekt definiert werden (nicht Bedingung), um damit den Zugriff und die Einbindung dieser in jedem Fall zu sichern. Beispiel: C++ Programm matbau6a.cpp enthält die Header-Files über die IncludeAnweisungen #include #include #include #include #include <stdio.h> <iostream.h> <malloc.h> <stdlib.h> <math.h> Das Projekt hat die kurze Darstellung · - proj0002[.exe] | · − 2 matbau6a[.cpp] 19 2. Arbeit mit vorhandenem Projekt. Als Beispiel diene das Programm M AWP.C im Verzeichnis ..\CNUM8\17\TST. (a) Datei, Öffnen von ..\CNUM8\17\TST\M AWP.C (b) Projekt, Projekt öffnen von ..\CNUM8\17\TST\PROJ0001.IDE Im Projektfenster ist folgender Baum mit den Files des Projekts. · · · · · · - proj0001[.exe] |− 2 m awp[.c] |− 2 t dgls[.c] |− 2 ..\awp[.c] |− 2 ..\..\basis\basis[.c] |− 2 ..\..\basis\vmblock[.c] (c) Debug, Ausführen (oder Strg F9 bzw. Blitz“-Ikone) ” Starten des Projekts 3. Zusammenfassung mehrerer unabhängiger Programme in einem Projekt. Als Beispiel dienen die Programme matbau6a.cpp ... matbau6e.cpp. Dazu kann man folgenden Baum im Projektfenster erzeugen. · · · · · · - proj0003[.exe] |− 2 matbau6a[.cpp] |− 2 matbau6b[.cpp] |− 2 matbau6c[.cpp] |− 2 matbau6d[.cpp] |− 2 matbau6e[.cpp] Da aber immer nur eines dieser Programme aktiv sein kann und ausgeführt werden kann, müssen die anderen mittels - Anglicken der rechten Maustaste, - Lokale Optionen bearbeiten ... (Edit local options) Optionen, Attribute - Vom Elternknoten ausschließen (Build Attributes, Exclude from parents) vom Elternknoten (hier matbau6a.cpp) ausgeschlossen werden. Anstelle der Markierung “·“ erhalten die ausgegrenzten Knoten die neue Markierung “ “. Die Darstellung im Projektfenster ist dann · - proj0003[.exe] · |− 2 matbau6a[.cpp] |− 2 matbau6b[.cpp] |− 2 matbau6c[.cpp] |− 2 matbau6d[.cpp] |− 2 matbau6e[.cpp] Natürlich kann auch jedes andere Programm so aktiviert werden. 20 4 Projekte in Dev-C++ Wir demonstrieren die Erstellung von Projekten und den Umgang damit anhand von einigen Beispielen. Dabei wollen wir schrittweise vorgehen unter Verwendung der Informationen aus den vorherigen Abschnitten. 4.1 Aufgabe 1 In den Demoprogrammen wird der Gauß-Algorithmus zur Lösung des LGS Ax = b in verschiedenen Versionen implementiert. Die Programme befinden sich im Verzeichnis C:\d\Neundorf\nwptexte\tech phy\05\dev cpp\projekt1. Variante 1 Demoprogramme gauss1.c und gauss1.cpp (außer den Dateinamen keine Unterschiede) /* /* /* /* /* gauss1.c bzw. gauss1.cpp */ Resttableau-Gauss-Algorithmus mit Spaltenpivotisierung und Zeilenvertauschung */ Tausch mit ersten NNE in der Spalte */ Ax=b, A=>oberes U, unteres Dreieck gleich Null */ Ux=c Loesung mit Rueckwaertseinsetzen */ #include <stdio.h> #include <stdlib.h> #include <math.h> #include <conio.h> #define Nmax 10 // Vowaertsdeklarationen, Prototypen int Gauss(); void Einlesen(); void Rausschreiben(); int SucheNichtNull(int); void NulleSpalte(int); void BerechneX(); void Vertausche(int, int); void Zeile_abziehen(int, int); int N; double A[Nmax][Nmax],b[Nmax],x[Nmax]; // statische Felder int main() { int flag; Einlesen(); flag = Gauss(); if(flag!=0) { printf("Gleichungssystem nicht loesbar!\n"); getch(); return(-1); } Rausschreiben(); return(0); } /*---------------------------------------------------------------------------*/ int Gauss() { int i,j,flag; for(i=0;i<N;i++) { flag = SucheNichtNull(i); if(flag!=0) return(-1); NulleSpalte(i); } BerechneX(); return(0); } 21 void Einlesen() { FILE *datin; int i,j; // Dateiarbeit datin = fopen("gauss1-input.dat","r"); fscanf(datin,"%i",&N); for(i=0;i<N;i++) for(j=0;j<N;j++) fscanf(datin,"%lf",&A[i][j]); for(i=0;i<N;i++) fscanf(datin,"%lf",&b[i]); fclose(datin); } /*---------------------------------------------------------------------------*/ void Rausschreiben() // Dateiarbeit { FILE *datout; int i,j; datout = fopen("gauss1-output.dat","w"); for(i=0;i<N;i++) fprintf(datout,"%lf\n",x[i]); fclose(datout); } /*---------------------------------------------------------------------------*/ int SucheNichtNull(int i) { int k; for(k=i;k<N;k++) if(A[k][i]!=0.0) { Vertausche(i,k); return(0); } return(-1); // kein NNE in der i-ten Spalte, A singulaer } /*---------------------------------------------------------------------------*/ void NulleSpalte(int i) { int k; for(k=i+1;k<N;k++) Zeile_abziehen(i,k); // i-tes Resttableau modifizieren } /*---------------------------------------------------------------------------*/ void BerechneX(void) // Rueckwaertseinsetzen fuer Loesung { int k,l; for(k=N-1;k>=0;k--) { x[k] = b[k]; for(l=k+1;l<N;l++) x[k] = x[k]-A[k][l]*x[l]; x[k] = x[k]/A[k][k]; } } /*---------------------------------------------------------------------------*/ void Vertausche(int i, int j) { double temp; int k; for(k=i;k<N;k++) { temp = A[i][k]; A[i][k] = A[j][k]; A[j][k] = temp; } temp = b[i]; b[i] = b[j]; b[j] = temp; } /*---------------------------------------------------------------------------*/ void Zeile_abziehen(int i, int j) { double quotient; int k; quotient = A[j][i]/A[i][i]; for(k=i;k<N;k++) // Resttableau modifizieren A[j][k] = A[j][k]-quotient*A[i][k]; // dabei i-te Spalte zu Null machen b[j] = b[j]-quotient*b[i]; } 22 Eingabedatei: gauss1-input.dat 2 0 2 1 4 1 0 Ausgabedatei: gauss1-output.dat 2.000000 1.000000 Variante 2 Demoprogramme gauss21.cpp, gauss22.cpp und gauss21.c Anders als bei Variante 1 wollen wir den Gauß-Algorithmus in Form des ResttableauAlgorithmus als kompakte Funktion und die Ein-/Ausgabe als selbständige Funktionen definieren und dabei die Möglichkeit der Eingabe- und Rückgabeparameter nutzen. Deshalb sind dazu einige Erläuterungen zum Parameterkonzept sinnvoll. Es gibt in C++/C mehrere Übergabeverfahren für Parameter. (a) Werteparameter, call by value Dies ist der übliche Weg, wo dem Parameter beim Aufruf der Funktion eine Variable übergeben wird. Der Parameter wird dann mit dem Wert dieser Variablen initialisiert und stellt eine Kopie der Variablen dar (erfordert zusätzlichen Speicherplatz), mit der die Funktion arbeiten kann. Die übergebene Variable wird nicht von der aufrufenden Funktion verändert. (b) Referenzparameter, call by reference Dabei wird als Argument eine Adresse übergeben, und über diese Adresse kann die aufrufende Funktion auf ein Objekt zugreifen. Dieses sogenannte var-Objekt ist sowohl in der Funktion als auch in ihrer Umgebung verfügbar. Vorgenommene Veränderungen in der Funktion werden damit auch an die Umgebung zuruckgegeben (Rückgabeparameter). Für Felder ist das Zeigerkonzept und damit die Adressübergabe fest in der Sprache verankert. Für einfache Variablen muss ein Zeiger-Parameter (speichert und verwaltet Adressen) verwendet werden, der auf das var-Objekt zugreift und es auch verändert. Drei Möglichkeiten der Verwendung von Zeigern als Parameter und damit der Übergabe von Adressen in Parametern gibt es. - Direkte Übergabe der Adresse einer Variablen (&variable), - analog dazu die Übergabe eines Zeigers, - Übergabe einer Referenz (nur C++). Betrachten wir die parameterabhängige Eingabefunktion void eingabe(int n, double a[Nmax][Nmax], double b[Nmax]) { int i,j; printf("Matrixdimension n = "); scanf("%d",&n); printf("Matrixeingabe element- und zeilenweise\n"); for(i=0;i<n;i++) for(j=0;j<n;j++) scanf("%lf",&a[i][j]); printf("Rechte Seite elementweise\n"); for(i=0;i<n;i++) scanf("%lf",&b[i]); } 23 Darin ist die Variable n ein Werteparameter sowie die Felder a und b automatisch Referenzparameter. Die Felder stehen nach ihrer Eingabe innerhalb der Funktion auch in der Umgebung (Rahmenprogramm) zur Verfügung. Die eingelesene Dimension n wird jedoch nicht zurückgegeben. Das soll nun verändert werden. Übergabeverfahren von Adresse oder Zeiger für C++/C Definition der Eingabefunktion void eingabe(int *n, double a[Nmax][Nmax], double b[Nmax]) { int i,j; printf("Matrixdimension n = "); scanf("%d",n); // genauso scanf("%d",&*n); printf("Matrixeingabe element- und zeilenweise\n"); for(i=0;i<*n;i++) for(j=0;j<*n;j++) scanf("%lf",&a[i][j]); printf("Rechte Seite elementweise\n"); for(i=0;i<*n;i++) scanf("%lf",&b[i]); } Aufruf der Eingabefunktion ... int n,abb,t,i,j; double a[Nmax][Nmax],b[Nmax],x[Nmax]; int ind[Nmax]; ... eingabe(&n,a,b); ... Übergabeverfahren von Referenz nur für C++ Definition der Eingabefunktion void eingabe(int &n, double a[Nmax][Nmax], double b[Nmax]) { int i,j; printf("Matrixdimension n = "); scanf("%d",&n); printf("Matrixeingabe element- und zeilenweise\n"); for(i=0;i<n;i++) for(j=0;j<n;j++) scanf("%lf",&a[i][j]); printf("Rechte Seite elementweise\n"); for(i=0;i<n;i++) scanf("%lf",&b[i]); } Aufruf der Eingabefunktion ... int n,abb,t,i,j; double a[Nmax][Nmax],b[Nmax],x[Nmax]; int ind[Nmax]; ... eingabe(n,a,b); ... Bemerkenswert ist die Syntax für die Funktion mit dem Referenzparameter. Die Funktion sieht der Werteparameter-Version sehr ähnlich. Nur in der Parameterdeklaration zeigt das Adresssymbol &, dass es sich um einen Referenzparameter handelt. Auch der Aufruf ist identisch. 24 Demoprogramm gauss21.c mit Übergabeverfahren von Zeiger (genauso gauss21.cpp mit Übergabeverfahren von Zeiger, analog gauss22.cpp mit Übergabeverfahren von Referenz) /* /* /* /* /* gauss21.c */ Resttableau-Gauss-Algorithmus mit Spaltenpivotisierung und Zeilenvertauschung */ Tausch mit betragsgroessten NNE in der Spalte */ Ax=b, A => L\U, P, PA=LU */ Ux=c Loesung mit Rueckwaertseinsetzen */ #include #include #include #include <stdio.h> <stdlib.h> <math.h> <conio.h> #define Nmax 10 // Ohne Prototypen, alle Definitionen stehen vor der main-Funktion void eingabe(int *n, double a[Nmax][Nmax], double b[Nmax]) { int i,j; printf("Matrixdimension n = "); scanf("%d",n); // // // // // Kontrolle printf("*n=%d\n",*n); printf("n=%d\n",n); printf("&*n=%d\n",&*n); getch(); // genauso richtig ist scanf("%d",&*n); // n // Adresse auf n // gleiches Ergebnis printf("Matrixeingabe element- und zeilenweise\n"); for(i=0;i<*n;i++) for(j=0;j<*n;j++) scanf("%lf",&a[i][j]); printf("Rechte Seite elementweise\n"); for(i=0;i<*n;i++) scanf("%lf",&b[i]); } void ausgabe(int n, double x[Nmax], int ind[Nmax]) { int i; printf("Loesungsvektor\n"); for(i=0;i<n;i++) printf("%lf ",x[i]); printf("\nPermutationsvektor\n"); for(i=0;i<n;i++) printf("%d ",1+ind[i]); printf("\n"); } void gauss(int n, int *t, double a[Nmax][Nmax], double b[Nmax], double x[Nmax], int ind[Nmax], int *sing) { double max,s,eps; int i,j,m,index,h; /* Initialisierung */ *sing = 0; for(m=0;m<n;m++) ind[m] = m; eps = 1e-12; /* Permutationsvektor */ /* Toleranz fuer Test auf Singularitaet */ /* Hinrechnung */ for(m=0;m<n;m++) { /* Pivotsuche */ max = 0.0; for(i=m;i<n;i++) { s = a[i][m]; if(fabs(s)>fabs(max)) { 25 max = s; index = i; } } /* Test auf Singularitaet */ if(fabs(max)<eps) { *sing = 1; *t = m; printf("\nMatrix singulaer, Abbruch im Schritt = %d\n",m+1); goto _LM1; } /* Zeilentausch */ if(index>m) { h = ind[m]; ind[m] = ind[index]; ind[index] = h; for(i=0;i<n;i++) { s = a[m][i]; a[m][i] = a[index][i]; a[index][i] = s; } s = b[m]; b[m] = b[index]; b[index] = s; } /* Bestimmung der Spaltenelemente und Zeilenelemente */ for(i=m+1;i<n;i++) { s = a[i][m]/max; a[i][m] = s; for(j=m+1;j<n;j++) a[i][j] = a[i][j]-s*a[m][j]; b[i] = b[i]-s*b[m]; } } /* Rueckrechnung */ for(i=n-1;i>=0;i--) { s = -b[i]; for(j=n-1;j>i;j--) s += a[i][j]*x[j]; x[i] = -s/a[i][i]; } _LM1: ; } int main() { int n,abb,t,i,j; double a[Nmax][Nmax],b[Nmax],x[Nmax]; int ind[Nmax]; printf("Gauss-Algorithmus: Resttableau-Algorithmus\n"); printf("mit Spaltenpivotisierung und Zeilenvertauschung\n\n"); eingabe(&n,a,b); gauss(n,&t,a,b,x,ind,&abb); if(abb==1) { printf("Gleichungssystem nicht loesbar!\n"); getch(); return(-1); } ausgabe(n,x,ind); getch(); return(0); } 26 Das Ergebnisfenster ist 2 C:\d\Neundorf\nwptexte\tech phy\05\dev cpp\projekt1\gauss21.exe Gauss-Algorithmus: Resttableau-Algorithmus mit Spaltenpivotisierung und Zeilenvertauschung Matrixdimension n = 2 Matrixeingabe element- und zeilenweise 1 2 4 6 Rechte Seite elementweise 1 1 Loesungsvektor -2.000000 1.500000 Permutationsvektor 2 1 ———————————————————————————————————Gauss-Algorithmus: Resttableau-Algorithmus mit Spaltenpivotisierung und Zeilenvertauschung Matrixdimension n = 2 Matrixeingabe element- und zeilenweise 1 2 1 2 Rechte Seite elementweise 1 1 Matrix singulaer, Abbruch im Schritt = 2 Gleichungssystem nicht loesbar! Im weiteren sollen zwei Programmiervarianten im Vordergrund stehen, nämlich die ZeigerÜbergabe in C, die auch in C++ machbar ist, sowie die Referenz-Übergabe in C++. • Als erstes verwenden wir für die 3 Funktionen eingabe, ausgabe, gauss Prototypen. C-Version mit Zeiger /* /* /* /* /* gauss2a.c */ Resttableau-Gauss-Algorithmus mit Spaltenpivotisierung und Zeilenvertauschung */ Tausch mit betragsgroessten NNE in der Spalte */ Ax=b, A => L\U, P, PA=LU */ Ux=c Loesung mit Rueckwaertseinsetzen */ #include <stdio.h> #include <stdlib.h> #include <math.h> #include <conio.h> #define Nmax 10 // mit Prototypen, Vorwaertsdeklarationen void eingabe(int*, double(*)[Nmax], double*); void gauss(int, int*, double(*)[Nmax], double*, double*, int*, int*); 27 // 1.Par. = Zeiger // 2.,7.Par. = Zeiger void ausgabe(int, double*, int*); int main() { int n,abb,t,i,j; double a[Nmax][Nmax],b[Nmax],x[Nmax]; int ind[Nmax]; printf("Gauss-Algorithmus: Resttableau-Algorithmus\n"); printf("mit Spaltenpivotisierung und Zeilenvertauschung\n\n"); eingabe(&n,a,b); gauss(n,&t,a,b,x,ind,&abb); if(abb==1) { printf("Gleichungssystem nicht loesbar!\n"); getch(); return(-1); } ausgabe(n,x,ind); getch(); return(0); } // Definitionen stehen nach der main-Funktion void eingabe(int *n, double a[Nmax][Nmax], double b[Nmax]) { int i,j; printf("Matrixdimension n = "); scanf("%d",n); printf("Matrixeingabe element- und zeilenweise\n"); for(i=0;i<*n;i++) for(j=0;j<*n;j++) scanf("%lf",&a[i][j]); printf("Rechte Seite elementweise\n"); for(i=0;i<*n;i++) scanf("%lf",&b[i]); } void ausgabe(int n, double x[Nmax], int ind[Nmax]) { int i; printf("\nLoesungsvektor\n"); for(i=0;i<n;i++) printf("%lf ",x[i]); printf("\nPermutationsvektor\n"); for(i=0;i<n;i++) printf("%d ",1+ind[i]); printf("\n"); } void gauss(int n, int *t, double a[Nmax][Nmax], double b[Nmax], double x[Nmax], int ind[Nmax], int *sing) { double max,s,eps; int i,j,m,index,h; /* Initialisierung */ *sing = 0; for(m=0;m<n;m++) ind[m] = m; /* Permutationsvektor */ eps = 1e-12; /* Toleranz fuer Test auf Singularitaet */ /* Hinrechnung */ for(m=0;m<n;m++) { /* Pivotsuche */ max = 0.0; for(i=m;i<n;i++) { s = a[i][m]; if(fabs(s)>fabs(max)) { max = s; index = i; } } /* Test auf Singularitaet */ if(fabs(max)<eps) { *sing = 1; *t = m; 28 printf("\nMatrix singulaer, Abbruch im Schritt %d\n",m+1); goto _LM1; } /* Zeilentausch */ if(index>m) { h = ind[m]; ind[m] = ind[index]; ind[index] = h; for(i=0;i<n;i++) { s = a[m][i]; a[m][i] = a[index][i]; a[index][i] = s; } s = b[m]; b[m] = b[index]; b[index] = s; } /* Bestimmung der Spaltenelemente und Zeilenelemente */ for(i=m+1;i<n;i++) { s = a[i][m]/max; a[i][m] = s; for(j=m+1;j<n;j++) a[i][j] = a[i][j]-s*a[m][j]; b[i] = b[i]-s*b[m]; } } /* Rueckrechnung */ for(i=n-1;i>=0;i--) { s = -b[i]; for(j=n-1;j>i;j--) s += a[i][j]*x[j]; x[i] = -s/a[i][i]; } _LM1: ; } C++-Version mit Referenz /* gauss2a.cpp */ ... // mit Prototypen, Vorwaertsdeklarationen void eingabe(int &, double(*)[Nmax], double*); // 1.Par. = Referenz void gauss(int, int &, double(*)[Nmax], double*, double*, int*, int &); // 2.,7.Par. = Referenz void ausgabe(int, double*, int*); int main() { int n,abb,t,i,j; double a[Nmax][Nmax],b[Nmax],x[Nmax]; int ind[Nmax]; printf("Gauss-Algorithmus: Resttableau-Algorithmus\n"); printf("mit Spaltenpivotisierung und Zeilenvertauschung\n\n"); eingabe(n,a,b); gauss(n,t,a,b,x,ind,abb); if(abb==1) { printf("Gleichungssystem nicht loesbar!\n"); getch(); return(-1); } ausgabe(n,x,ind); getch(); return(0); } // Definitionen stehen nach der main-Funktion void eingabe(int &n, double a[Nmax][Nmax], double b[Nmax]) ... void ausgabe(int n, double x[Nmax], int ind[Nmax]) ... void gauss(int n, int &t, double a[Nmax][Nmax], double b[Nmax], double x[Nmax], int ind[Nmax], int &sing) ... 29 • Als zweites erzeugen wir zu den Prototypen für die 3 Funktionen eingabe, ausgabe, gauss eine Header-Datei und nehmen diese ins Rahmenprogramm auf. C-Version: Header-Datei gauss2b.h. /* gauss2b.h */ /* Resttableau-Gauss-Algorithmus mit Spaltenpivotisierung und Zeilenvertauschung */ #include <stdio.h> #include <stdlib.h> #include <math.h> #include <conio.h> #define Nmax 10 // mit Prototypen, Vorwaertsdeklarationen void eingabe(int*, double(*)[Nmax], double*); void gauss(int, int*, double(*)[Nmax], double*, double*, int*, int*); void ausgabe(int, double*, int*); // 1.Par. = Zeiger // 2.,7.Par. = Zeiger Diese liegt im selben Verzeichnis wie das einschließende Programm gauss2b.c und wird dort verwendet. /* gauss2b.c */ /* Resttableau-Gauss-Algorithmus mit Spaltenpivotisierung und Zeilenvertauschung */ #include #include #include #include #include <stdio.h> <stdlib.h> <math.h> <conio.h> "gauss2b.h" // Header-Datei im aktuellen Verzeichnis // Definitionen und Prototypen eingabe, gauss, ausgabe int main() ... // Definitionen der Prototypen stehen nach der main-Funktion ... C++-Version: Header-Datei gauss2bp.h. /* gauss2bp.h */ /* Resttableau-Gauss-Algorithmus mit Spaltenpivotisierung und Zeilenvertauschung */ #include <stdio.h> #include <stdlib.h> #include <math.h> #include <conio.h> #define Nmax 10 // mit Prototypen, Vorwaertsdeklarationen void eingabe(int &, double(*)[Nmax], double*); void gauss(int, int &, double(*)[Nmax], double*, double*, int*, int &); void ausgabe(int, double*, int*); // 1.Par. = Referenz // 2.,7.Par. = Referenz Diese liegt im selben Verzeichnis wie das einschließende Programm gauss2b.cpp und wird dort verwendet. /* gauss2b.cpp */ /* Resttableau-Gauss-Algorithmus mit Spaltenpivotisierung und Zeilenvertauschung */ #include #include #include #include #include <stdio.h> <stdlib.h> <math.h> <conio.h> "gauss2bp.h" // Header-Datei im aktuellen Verzeichnis // Definitionen und Prototypen eingabe, gauss, ausgabe int main() ... // Definitionen der Prototypen stehen nach der main-Funktion ... 30 • Als dritten und letzten Schritt nehmen wir die Header-Datei zu den 3 Prototypen, bilden aus deren Definitionen einen eigenständigen Quelltext, um schließlich mit dem Rahmenprogramm ein Projekt daraus zu konstruieren. C-Version: Header-Datei gauss2b.h. Quelltext gauss2cc.c. /* gauss2cc.c */ /* Resttableau-Gauss-Algorithmus mit Spaltenpivotisierung und Zeilenvertauschung */ #include "gauss2b.h" // Header-Datei im aktuellen Verzeichnis // Definitionen und Prototypen eingabe, gauss, ausgabe // Definitionen der Prototypen als eigenstaendiger Quelltext void eingabe(int *n, double a[Nmax][Nmax], double b[Nmax]) ... void ausgabe(int n, double x[Nmax], int ind[Nmax]) ... void gauss(int n, int *t, double a[Nmax][Nmax], double b[Nmax], double x[Nmax], int ind[Nmax], int *sing) ... Rahmenprogramm (Quelltext mit main-Funktion) gauss2c.c. /* gauss2c.c */ /* Resttableau-Gauss-Algorithmus mit Spaltenpivotisierung und Zeilenvertauschung */ #include #include #include #include #include <stdio.h> <stdlib.h> <math.h> <conio.h> "gauss2b.h" // Header-Datei im aktuellen Verzeichnis // Definitionen und Prototypen eingabe, gauss, ausgabe int main() ... Projekt gauss2c mit Rahmenprogramm gauss2c.c, Quelltext gauss2cc.c, Header-Datei gauss2b.h Wir erstellen dieses Projekt. Die einzelnen Schritte seien grob umrissen. 1. Zunächst ist es sinnvoll, alle Dateien zu schließen. Menüpunkt Datei ⇒ Schließen oder Alles Schließen 2. Dann erstellt man das neue Projekt. Menüpunkt Datei ⇒ Neu ⇒ Projekt Auswahlfenster Neues Projekt Basic ⇒ Empty Project —– Projekt Optionen: ————————————————— Name: • C-Projekt gauss2c Ok 31 3. Damit öffnet sich ein neues Auswahlfenster Create new project Speichern in: C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\projekt1 Dateiname: Dateityp: gauss2c dev-c++ project(*.dev) Speichern Es erscheint unter Projekt im Projektfenster folgender Baum des noch leeren Projekts gauss2c. S ℵ≡ gauss2c Gleichzeitig wird im Verzeichnis die Projektdatei gauss2c.dev mit folgendem Inhalt angelegt. [Project] filename=C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\projekt1\gauss2c.dev name=hilf Folders= CommandLine= Order= [Views] ProjectView=1 4. Nun vervollständigt man das Projekt um die Komponenten gauss2c.c und gauss2cc.c. Menüpunkt Projekt ⇒ Zum Projekt hinzufügen Damit öffnet sich das Auswahlfenster Unit öffnen Suchen in: projekt1 (aktuelles Verzeichnis) Dateiname: Dateityp: Öffnen gauss2c All known Files Genauso mit gauss2cc. Im Projektfenster vergrößert sich der Baum des Projekts gauss2c. Es erscheint S 2- –ℵ≡ gauss2c ||− gauss2c.c |− gauss2cc.c Hinzugefügte Komponenten können durch Anklicken als Quelltexte angezeigt werden. Gleichzeitig vergrößert sich im Verzeichnis die Projektdatei gauss2c.dev. [Project] FileName=gauss2c.dev Name=gauss2c UnitCount=2 Type=1 Ver=1 ObjFiles= Includes= Libs= PrivateResource= ResourceIncludes= MakeIncludes= Resources= Compiler= Linker= IsCpp=0 Icon= ExeOutput= ObjectOutput= OverrideOutput=0 32 OverrideOutputName= Folders= CommandLine= Focused=1 Order=0,1 [Unit1] FileName=gauss2c.c Open=1 Folder= Top=0 CursorCol=1 CursorRow=1 TopLine=1 LeftChar=1 [Unit2] FileName=gauss2cc.c Open=1 Folder= Top=1 CursorCol=1 CursorRow=1 TopLine=1 LeftChar=1 [Views] ProjectView=1 Das Projekt kann also konfiguriert werden, indem insbesondere überflüssige Knoten (Programme) gelöscht und neue Knoten hinzugefügt werden (hierarchische Struktur). 5. Es ist manchmal sinnvoll bzw. notwendig, Verzeichnisse von eingebundenen Dateien (C-Includes, C++-Includes) zu ergänzen. Menüpunkt Werkzeuge ⇒ Compiler Optionen Damit öffnet sich das Auswahlfenster Compiler Optionen Unter Verzeichnisse und C-Includes kann im Fenster das Verzeichnis C:\d\neundorf\nwptexte\tech_phy\05\dev_cpp\projekt1 eingetragen werden. Dann Hinzufügen und Ok . 6. Man klickt nun auf das Projekt gauss2c und kommt zum • Menüpunkt Ausführen ⇒ Kompilieren Es folgen Informationen zum Übersetzen und im Kompilier Log steht Building Makefile: "C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\projekt1\Makefile.win" Fuehrt make... aus make.exe -f "C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\projekt1\Makefile.win" all gcc.exe -c gauss2c.c -o gauss2c.o -I"C:/Dev-Cpp/include" -I"c:/d/Neundorf/nwptexte/tech_phy/05/dev_cpp/projekt1" -s gcc.exe -c gauss2cc.c -o gauss2cc.o -I"C:/Dev-Cpp/include" -I"c:/d/Neundorf/nwptexte/tech_phy/05/dev_cpp/projekt1" gcc.exe gauss2c.o gauss2cc.o -o "gauss2c.exe" -L"C:/Dev-Cpp/lib" -I"C:/Dev-Cpp/include" -I"c:/d/Neundorf/nwptexte/tech_phy/05/dev_cpp/projekt1" Ausfuehrung beendet Kompilierung erfolgreich 33 -s -s Im Verzeichnis stehen dann die ausführbare Datei gauss2c.exe sowie die Objektdateien gauss2c.o und gauss2cc.o. Sind die Objektdateien zu diesem Projekt schon vorhanden (z. B. durch wiederholten Aufruf), so müssen diese nicht erzeugt werden und die Information verkürzt sich entsprechend. Legt man im Verzeichnis jedoch ein weiteres Projekt an, das zufällig dieselben Namen von Objektdateien verwendet, sollte man zuvor die “alten“ Objekte löschen. Dazu kommt zum Projekt noch das sogenannte Makefile Makefile.win. # Project: gauss2c # Makefile created by Dev-C++ 4.9.6.0 CC = gcc.exe WINDRES = windres.exe RES = OBJ = gauss2c.o gauss2cc.o $(RES) LIBS = -L"C:/Dev-Cpp/lib" INCS = -I"C:/Dev-Cpp/include" -I"C:/d/Neundorf/nwptexte/tech_phy/05/dev_cpp/projekt1" BIN = gauss2c.exe CFLAGS = $(INCS) -s .PHONY: all all-before all-after clean clean-custom all: all-before gauss2c.exe all-after clean: clean-custom rm -f $(OBJ) $(BIN) $(BIN): $(OBJ) $(CC) $(OBJ) -o "gauss2c.exe" $(LIBS) $(CFLAGS) gauss2c.o: gauss2c.c $(CC) -c gauss2c.c -o gauss2c.o $(CFLAGS) gauss2cc.o: gauss2cc.c $(CC) -c gauss2cc.c -o gauss2cc.o $(CFLAGS) Das Makefile zeigt an, wie das Projekt aufgebaut ist, wie es abgearbeitet wir, welche Abhängigkeiten zwischen einzelnen Programmmodulen bestehen und welche Programmbibliotheken eingebunden sind. Unter Unix bzw. Linux kann es einfach mit dem Kommando make aufgerufen werden. Unter Windows gibt es dies nicht. • Menüpunkt Ausführen ⇒ Ausführen Es folgt das Ausführen des Programms mit den Fenster für Ein- und Ausgaben. • Menüpunkt Ausführen ⇒ Kompilieren u. Ausführen Es folgen Informationen zum Übersetzen und im Kompilier Log steht Building Makefile: "C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\projekt1\Makefile.win" Fuehrt make... aus make.exe -f "C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\projekt1\Makefile.win" all make.exe: Nothing to be done for ‘all’. Ausfuehrung beendet Kompilierung erfolgreich Diese Kurzform ergibt sich, wenn das Makefile schon vorhanden ist. 34 C++-Version: Header-Datei gauss2bp.h. Quelltext gauss2cc.cpp. /* gauss2cc.cpp */ /* Resttableau-Gauss-Algorithmus mit Spaltenpivotisierung und Zeilenvertauschung */ #include "gauss2bp.h" // Header-Datei im aktuellen Verzeichnis // Definitionen und Prototypen eingabe, gauss, ausgabe // Definitionen der Prototypen als eigenstaendiger Quelltext void eingabe(int &n, double a[Nmax][Nmax], double b[Nmax]) ... void ausgabe(int n, double x[Nmax], int ind[Nmax]) ... void gauss(int n, int &t, double a[Nmax][Nmax], double b[Nmax], double x[Nmax], int ind[Nmax], int &sing) ... Rahmenprogramm (Quelltext mit main-Funktion) gauss2c.cpp. /* gauss2c.cpp */ /* Resttableau-Gauss-Algorithmus mit Spaltenpivotisierung und Zeilenvertauschung */ #include #include #include #include #include <stdio.h> <stdlib.h> <math.h> <conio.h> "gauss2bp.h" // Header-Datei im aktuellen Verzeichnis // Definitionen und Prototypen eingabe, gauss, ausgabe int main() ... Projekt gauss2cpp mit Rahmenprogramm gauss2c.cpp, Quelltext gauss2cc.cpp, Header-Datei gauss2bp.h Der Umgang mit diesem Projekt, das hier dasselbe Verzeichnis benutzen soll, ist sehr ähnlich zum vorhergehenden Projekt gauss2c. Es kann aber immer nur ein Projekt bearbeitet werden. Ein Unterschied besteht darin, dass man es natürlich mit C++ zu tun hat und der Compiler g++ verwendet wird. Dann beachte man die Modifikationen in den Funktionen eingabe, gauss bezüglich der Verwendung von Referenzparametern. Da die Komponenten des Projekts gauss2cpp dieselben Bezeichner haben wie die vom Projekt gauss2c, ist es ratsam vor der Kompilierung bestehende “alte“ Objektdateien gauss2c.o und gauss2cc.o sicherheitshalber zu löschen. Im vorliegenden Fall kann jedoch jedes der beiden Projekte jeweils mit den Objektdateien des anderen arbeiten. Im Projektfenster steht der Baum des Projekts gauss2cpp. S 2- –ℵ≡ gauss2cpp ||− gauss2c.cpp |− gauss2cc.cpp Klickt man den Schalter Klassen an, so erscheinen im Projektfenster die Klassen (hier Funktionen) main, ausgabe, eingabe, gauss. 35 Variante 3 Demoprogramme gauss31.cpp, gauss32.cpp und gauss31.c Ähnlich zur Variante 2 wollen wir den Gauß-Algorithmus in Form des verketteten Algorithmus als kompakte Funktion und die Ein-/Ausgabe als selbständige Funktionen definieren und dabei die schon beschriebenen Möglichkeiten der Eingabe- und Rückgabeparameter nutzen. Demoprogramm gauss31.c mit Übergabeverfahren von Zeiger (genauso gauss31.cpp mit Übergabeverfahren von Zeiger, analog gauss32.cpp mit Übergabeverfahren von Referenz) /* /* /* /* /* gauss31.c */ Verketteter Gauss-Algorithmus mit Spaltenpivotisierung und Zeilenvertauschung */ Tausch mit betragsgroessten NNE in der Spalte */ Ax=b, A => (-L)\U, P, PA=LU */ Ux=c Loesung mit Rueckwaertseinsetzen */ #include <stdio.h> #include <stdlib.h> #include <math.h> #include <conio.h> #define Nmax 10 // Ohne Prototypen, alle Definitionen stehen vor der main-Funktion void eingabe(int *n, double a[Nmax][Nmax], double b[Nmax]) { int i,j; printf("Matrixdimension n = "); scanf("%d",n); // genauso richtig ist printf("Matrixeingabe element- und zeilenweise\n"); for(i=0;i<*n;i++) for(j=0;j<*n;j++) scanf("%lf",&a[i][j]); printf("Rechte Seite elementweise\n"); for(i=0;i<*n;i++) scanf("%lf",&b[i]); scanf("%d",&*n); } void ausgabe(int n, double x[Nmax], int ind[Nmax]) { int i; printf("\nLoesungsvektor\n"); for(i=0;i<n;i++) printf("%lf ",x[i]); printf("\nPermutationsvektor\n"); for(i=0;i<n;i++) printf("%d ",1+ind[i]); printf("\n"); } void gauss(int n, int *t, double a[Nmax][Nmax], double b[Nmax], double x[Nmax], int ind[Nmax], int *sing) { double max,s,eps; int i,j,m,index,h; /* Initialisierung */ *sing = 0; for(m=0;m<n;m++) ind[m] = m; eps = 1e-12; /* Hinrechnung */ for(m=0;m<n;m++) { /* Pivotsuche */ max = 0.0; for(i=m;i<n;i++) { s = a[i][m]; for(j=0;j<m;j++) s += a[i][j]*a[j][m]; a[i][m] = s; if(fabs(s)>fabs(max)) { max = s; /* Permutationsvektor */ /* Toleranz fuer Test auf Singularitaet */ 36 index = i; } } /* Test auf Singularitaet */ if(fabs(max)<eps) { *sing = 1; *t = m; printf("\nMatrix singulaer, Abbruch im Schritt %d\n",m+1); goto _LM1; } /* Zeilentausch */ if(index>m) { h = ind[m]; ind[m] = ind[index]; ind[index] = h; for(i=0;i<n;i++) { s = a[m][i]; a[m][i] = a[index][i]; a[index][i] = s; } s = b[m]; b[m] = b[index]; b[index] = s; } /* Bestimmung der Spaltenelemente */ for(i=m+1;i<n;i++) a[i][m] = -a[i][m]/max; /* Bestimmung der Zeilenelemente */ for(i=m+1;i<n;i++) { s = a[m][i]; for(j=0;j<m;j++) s += a[m][j]*a[j][i]; a[m][i] = s; } s = b[m]; for(j=0;j<m;j++) s += a[m][j]*b[j]; b[m] = s; } /* Rueckrechnung */ for(i=n-1;i>=0;i--) { s = -b[i]; for(j=n-1;j>i;j--) s += a[i][j]*x[j]; x[i] = -s/a[i][i]; } _LM1: ; } int main() { int n,abb,t,i,j; double a[Nmax][Nmax],b[Nmax],x[Nmax]; int ind[Nmax]; printf("Verketteter Gauss-Algorithmus\n"); printf("mit Spaltenpivotisierung und Zeilenvertauschung\n\n"); eingabe(&n,a,b); gauss(n,&t,a,b,x,ind,&abb); if(abb == 1) { printf("Gleichungssystem nicht loesbar!\n"); getch(); return(-1); } ausgabe(n,x,ind); getch(); return(0); } Weitere Betrachtungen kann man entsprechend der Vorgehensweise in Variante 2 machen, so dass auf Einzelheiten verzichtet wird. Im Prinzip findet nur die Ersetzung des Resttableau-Gauß-Algorithmus durch den verketteten Gauß-Algorithmus statt. 37 4.2 Aufgabe 2 Aus der Aachener Bibliothek wollen wir den Komplex LGS mit Gauß-Algorithmus auswählen. Das Demoprogramm TGAUSS.C befindet sich im Verzeichnis \CNUM8\04\TST und umfasst inhaltlich mehr als den Gauß-Algorithmus. Die dortigen Include-Anweisungen für die Header-Dateien #include <basis.h> #include <u_proto.h> #include <vmblock.h> /*Grundlegender Deklarationsteil, Funktionskoepfe*/ /*Vordeklaration aller Bibliotheksfunktionen*/ /*Dynamische Vektoren und Matrizen*/ enthalten schon wichtige Hinweise dafür, welche Files zum Projekt gehören. Dazu kommen also noch die Dateien \CNUM8\04\FGAUSS.C \CNUM8\BASIS\BASIS.C \CNUM8\BASIS\VMBLOCK.C \CNUM8\04\EIN /*Direkte Verfahren zur Loesung LGS*/ /*u.a. Gauss-Algorithmus mit Varianten*/ /*grundlegende Funktionen: Definitionsdatei*/ /*Verwaltung von dynamischen Vektoren und Matrizen*/ /*Beispielmatrizen, hier nicht genutzt*/ Man erkennt eventuell die benötigten Files aus den Include-Anweisungen der Anwendungsbeispiele. Der Einfachheit halber kopiert man alle diese Files in das Arbeitsverzeichnis C:\d\Neundorf\nwptexte\tech phy\05\dev cpp\projekt2 Dabei soll die Kleinschreibung der Bezeichner verwendet werden. Nun stellt man das C-Projekt gemäß der Vorgehensweise in Abschnitt 4.1 zusammen. Projekt tgauss mit Rahmenprogramm tgauss.c, Quelltexte fgauss.c, basis.c, vmblock.c, Header-Dateien basis.h, u proto.h, vmblock.h Im Projektfenster steht der Baum des Projekts tgauss. S 2- –ℵ≡ tgauss ||− basis.c ||− fgauss.c | tgauss.c |− |− vmblock.c Klickt man den Schalter Klassen an, so erscheint im Projektfenster eine umfangreiche Liste von Klassen und Funktionen zum Projekt. Man klickt nun auf das Projekt tgauss und kommt zum • Menüpunkt Ausführen ⇒ Kompilieren u. Ausführen Es folgen Informationen zum Übersetzen und im Kompilier Log steht Building Makefile: "C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\projekt2\Makefile.win" Fuehrt make... aus make.exe -f "C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\projekt2\Makefile.win" all 38 gcc.exe -c basis.c -o basis.o -I"C:/Dev-Cpp/include" -I"C:/d/Neundorf/nwptexte/tech_phy/05/dev_cpp/projekt2" -s gcc.exe -c vmblock.c -o vmblock.o -I"C:/Dev-Cpp/include" -I"C:/d/Neundorf/nwptexte/tech_phy/05/dev_cpp/projekt2" -s gcc.exe -c fgauss.c -o fgauss.o -I"C:/Dev-Cpp/include" -I"C:/d/Neundorf/nwptexte/tech_phy/05/dev_cpp/projekt2" -s gcc.exe -c tgauss.c -o tgauss.o -I"C:/Dev-Cpp/include" -I"C:/d/Neundorf/nwptexte/tech_phy/05/dev_cpp/projekt2" -s gcc.exe basis.o vmblock.o fgauss.o tgauss.o -o "tgauss.exe" -L"C:/Dev-Cpp/lib" -I"C:/Dev-Cpp/include" -I"c:/d/Neundorf/nwptexte/tech_phy/05/dev_cpp/projekt2" -s Ausfuehrung beendet Kompilierung erfolgreich Im Verzeichnis stehen dann die Projektdatei tgauss.dev, die ausführbare Datei tgauss.exe sowie die Objektdateien tgauss.o, fgauss.o, basis.o, vmblock.o. Dazu kommt zum Projekt noch das sogenannte Makefile Makefile.win. # Project: tgauss # Makefile created by Dev-C++ 4.9.6.0 CC = gcc.exe WINDRES = windres.exe RES = OBJ = basis.o vmblock.o fgauss.o tgauss.o $(RES) LIBS = -L"C:/Dev-Cpp/lib" INCS = -I"C:/Dev-Cpp/include" -I"C:/d/Neundorf/nwptexte/tech_phy/05/dev_cpp/projekt2" BIN = tgauss.exe CFLAGS = $(INCS) -s .PHONY: all all-before all-after clean clean-custom all: all-before tgauss.exe all-after clean: clean-custom rm -f $(OBJ) $(BIN) $(BIN): $(OBJ) $(CC) $(OBJ) -o "tgauss.exe" $(LIBS) $(CFLAGS) basis.o: basis.c $(CC) -c basis.c -o basis.o $(CFLAGS) vmblock.o: vmblock.c $(CC) -c vmblock.c -o vmblock.o $(CFLAGS) fgauss.o: fgauss.c $(CC) -c fgauss.c -o fgauss.o $(CFLAGS) tgauss.o: tgauss.c $(CC) -c tgauss.c -o tgauss.o $(CFLAGS) Folgende Berechnungen können im Programm durchgeführt werden: – inverse Matrix, – transponierte Inverse, – Gauß-Algorithmus mit rechter Seite als Einheitsvektoren, dazu Determinante und Kontrolle der Spur der Matrix AA−1 , – Gauß-Algorithmus für LGS mit Spaltenpivotisierung und Zeilenvertauschung, dazu LU -Faktorisierung von P A (zeilenvertauschte Matrix) und Permutationsvektor, – Gauß-Algorithmus für LGS mit Nachiteration zur Verbesserung der Näherungslösung. 39 Beispiel Ax = b 1 2 1 −2 ∗ −1 A= , b= , x =A b= 4 6 1 1.5 det(A) = −2, A = −3 1 , A = 2 −0.5 0 1 p = (2, 1), P = 1 0 −1 n X 1 2 aii , , trace(A) = spur(A) = 0 −2 i=1 −3 2 −T A = 1 −0.5 4 6 1 0 4 6 = LU = , PA = 0 0.5 0.25 1 1 2 1 0 4 1 Eingabe- und Ergebnisfenster Haltepunkte wie getch(); sind bei der Ausgabe großer Datenmengen als auch nach Fehlermeldungen sinvoll. 2 C:\d\Neundorf\nwptexte\tech phy\05\dev cpp\projekt2\tgauss.exe ——————————————————————————————– Gauss Method ——————————————————————————————– Dimension of the input quadratic matrix n = 2 Input matrix (elementwise and rowwise) 1 2 4 6 Dimension of the input matrix = 2 Input matrix: 1.000000 4.000000 2.000000 6.000000 Transposed Inverse: -3.000000 1.000000 2.000000 -0.500000 ——————————————————————————————– Gauss Method for multiple right sides ——————————————————————————————– Right sides = identity matrix Inverse: -3.000000 2.000000 1.000000 -0.500000 Determinante = -2.000000e+000 Trace (Matrix * Inverse) = 2.0000000000000000e+000 40 ——————————————————————————————– Solution without iterative improvement, one right side vector ——————————————————————————————– Input vector (right side elementwise): 1 1 A: 1.000000 4.000000 2.000000 6.000000 1.000000 1.000000 4.000000 0.250000 6.000000 0.500000 Solution: x: -2.000000 1.500000 b: LU: perm: 1 0 ——————————————————————————————– Solution with iterative improvement ——————————————————————————————– Input vector (right side elementwise): 1 1 A: 1.000000 4.000000 2.000000 6.000000 1.000000 1.000000 4.000000 0.250000 6.000000 0.500000 b: LU: perm: 1 0 Solution x: -2.000000 1.500000 ——————————————————————————————– Solution with post iteration for right sides = identity vectors ——————————————————————————————– b: x: 1.000000 -3.000000 0.000000 2.000000 b: x: 0.000000 1.000000 1.000000 -0.500000 ——————————————————————————————– 41 4.3 Aufgabe 3 In Abschnitt 1, Aufgabe 5, haben wir das Newtonsche Näherungsverfahren für Nullstellen von skalaren Funktionen als Demoprogramm tnewton1.c vorgestellt. Die Definitionen und Prototypen, also die Prozedurköpfe usw., findet man in den HeaderFiles basis.h, u_proto.h, tfunc1.h als Vorabdeklarationen und werden damit bekannt gemacht. Ihre eigentliche Definition erfolgt in den Funktionen fnewtonc.c, tfunc1.c, basis.c. Dazu brauchen wir die Dateien \CNUM8\02\TST\TNEWTON.C \CNUM8\02\TST\TFUNC1.C \CNUM8\02\FNEWTON.C \CNUM8\BASIS\BASIS.C /*Test program for newton*/ /*Definition of test functions for newton, pegasus,..*/ /*1. and 2. derivity*/ /*Numerische Verfahren zur Loesung NLG: NV,...*/ /*grundlegende Funktionen: Definitionsdatei*/ Daraus machen wir nun das C-Projekt tnewton im Verzeichnis C:\d\Neundorf\nwptexte\tech phy\05\dev cpp\projekt3, in das alle Dateien (Bezeichner klein geschrieben) kopiert werden. Projekt tnewton mit Rahmenprogramm tnewton.c, Quelltexte tfunc1.c, fnewton.c, basis.c, Header-Dateien basis.h, u proto.h, tfunc1.h Im Projektfenster steht der Baum des Projekts tnewton. S 2- –ℵ≡ tnewton ||− basis.c ||− fnewton.c | tfunc1.c |− |− tnewton.c Man klickt nun auf das Projekt tnewton und kommt zum • Menüpunkt Ausführen ⇒ Kompilieren u. Ausführen Es folgen Informationen zum Übersetzen, es entstehen im Verzeichnis die entsprechenden Dateien *.o, *.exe und im Kompilier Log steht Building Makefile: "C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\projekt3\Makefile.win" Fuehrt make... aus make.exe -f "C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\projekt3\Makefile.win" all gcc.exe -c basis.c -o basis.o -I"C:/Dev-Cpp/include" -I"C:/d/Neundorf/nwptexte/tech_phy/05/dev_cpp/projekt3" gcc.exe -c fnewton.c -o fnewton.o -I"C:/Dev-Cpp/include" -I"C:/d/Neundorf/nwptexte/tech_phy/05/dev_cpp/projekt3" gcc.exe -c tfunc1.c -o tfunc1.o -I"C:/Dev-Cpp/include" -I"C:/d/Neundorf/nwptexte/tech_phy/05/dev_cpp/projekt3" gcc.exe -c tnewton.c -o tnewton.o -I"C:/Dev-Cpp/include" -I"C:/d/Neundorf/nwptexte/tech_phy/05/dev_cpp/projekt3" gcc.exe basis.o fnewton.o tfunc1.o tnewton.o -o "tnewton.exe" -L"C:/Dev-Cpp/lib" -I"C:/Dev-Cpp/include" -I"C:/d/Neundorf/nwptexte/tech_phy/05/dev_cpp/projekt3" Ausfuehrung beendet Kompilierung erfolgreich 42 -s -s -s -s -s Das Ergebnisfenster ist 2 C:\d\Neundorf\nwptexte\tech phy\05\dev cpp\tnewton1.exe ——————————————————————————– Newton Method for real, nonlinear functions ——————————————————————————– f = 5∗x-exp(x) Start value x0 = 2.000000 Return code = 1 Root = 2.5426413577735265 Function value = -3.448110e-014 Iterations =7 f = (((((x-6)∗x+15)∗x-20)∗x+15)∗x-6)∗x+1 Start value x0 = 1.000000 Return code = 0 Root = 1.0000000000000000 Function value = 0.000000e+000 Iterations =0 f = sin(x) Start value x0 = 2.000000 Return code = 0 Root = 3.1415926535897931 Function value = 1.224606e-016 Iterations =6 f = 1+sin(x) Start value x0 = 2.000000 Return code = 0 Root = 4.7123890100448769 Function value = 4.398608e-016 Iterations = 26 f = exp(x)-(1.0+x+x∗x∗0.5) Start value x0 = 1.000000 Return code = 0 Root = 0.0000143482588343 Function value = 3.555099e-016 Iterations = 170 f = (x-1.0)∗(x-1.0)∗(sin(PI∗x)-log(fabs(2.0∗x/(x+1.0))) Start value x0 = 1.000000 Return code = 0 Root = 1.0000000000000000 Function value = 0.000000e+000 Iterations =0 ——————————————————————————– 43 4.4 Aufgabe 4 Nun soll ein etwas umfangreicheres Projekt zur Lösung LGS mit den Gauß-Algorithmus zusammengestellt werden. Der zentrale Datentyp ist ein Zeiger auf ein zweidimensionales Feld (Matrix) mit Komponenten vom Typ double. Vektoren werden in dieser Konvention als Feld mit einer Spalte interpretiert. Der Datentyp laMatrix zeigt auf eine Struktur, die als Informationen die Zeilen- und Spaltenmdimension sowie die Matrixelemente zeilenweise enthält. Die Nummerierung der Zeilen und Spalten beginnt in C/C++ jeweils bei Null. Der Resttableau-Gauß-Algorithmus mit Spaltenpivotisierung und Zeilenvertauschung aus Abschnitt 4.1 (vergl. Demoprogramm gauss21.c mit Übergabeverfahren von Zeiger) muss auf diesen Datentyp angepasst werden. Die Koeffizientenmatrix A ist Eingabematrix und wird überschrieben durch die LU -Zerlegung von P A, welche dann zurückgegeben wird. Deshalb werden Kopien von A und b angelegt. Der Lösungsvektor x und der Permutationsvektor von p=perm für die Zeilenvertauschungen sind einspaltige reelle Ergebnismatrizen. Die Komponenten von p sind wertemäßig natürliche Zahlen, was in der formatierten Ausgabe berücksichtigt wird. Falls der Gauß-Algorithmus nicht durchführbar ist, so weist der Ergebnisparameter sing6= 0 darauf hin, wobei mit der Größe t noch die Stufe (Schritt im Resttableau-Algorithmus) des Abbruchs angezeigt werden kann. Gleichzeitig ist der Stand der Berechnungen bis zum eventuellen Abbruch abfragbar. Die Toleranz ε=eps für den Test auf Singularität ist in der Funktion definiert und gegebenenfalls zu verändern. void gauss4c(int n, int *t, laMatrix *A, laMatrix b, laMatrix *x, laMatrix *perm, int *sing) { double max,s,eps,h; int i,j,k,index; /* Initialisierung */ *sing = 0; *t = 0; for(k=0;k<n;k++) { (*perm)[k][0] = k; (*x)[k][0] = 0; } eps = 1e-15; /* Permutationsvektor */ /* Toleranz fuer Test auf Singularitaet */ /* Hinrechnung */ for(k=0;k<n;k++) { /* Pivotsuche */ max = 0; for(i=k;i<n;i++) { s = fabs((*A)[i][k]); if(s>max) { max = s; index = i; } } /* Test auf Singularitaet */ if(max<eps) { *sing = 1; *t = k+1; printf("\nMatrix singulaer, Abbruch im Schritt = %d\n",k+1); goto _LM1; } 44 /* Zeilentausch */ if(index>k) { h = (*perm)[k][0]; /* laVertauscheZe_(*perm,k,index); */ (*perm)[k][0] = (*perm)[index][0]; (*perm)[index][0] = h; /* for(i=0;i<n;i++) { s = (*A)[k][i]; (*A)[k][i] = (*A)[index][i]; (*A)[index][i] = s; } */ laVertauscheZe_(*A,k,index); s = b[k][0]; b[k][0] = b[index][0]; b[index][0] = s; /* laVertauscheZe_(b,k,index); */ } /* Bestimmung der Spaltenelemente und Zeilenelemente */ max = (*A)[k][k]; for(i=k+1;i<n;i++) { s = (*A)[i][k]/max; (*A)[i][k] = s; for(j=k+1;j<n;j++) (*A)[i][j] = (*A)[i][j]-s*(*A)[k][j]; b[i][0] = b[i][0]-s*b[k][0]; } } /* Rueckrechnung */ for(i=n-1;i>=0;i--) { s = -b[i][0]; for(j=n-1;j>i;j--) s += (*A)[i][j]*(*x)[j][0]; (*x)[i][0] = -s/(*A)[i][i]; } _LM1: for(k=0;k<n;k++) (*perm)[k][0] = 1+(*perm)[k][0]; } In der Funktion gauss4c erkennt man den Funktionsaufruf laVertauscheZe_(*A,k,index) zum Tausch der Pivotzeile “nach oben“. Diese, noch weitere Module zur linearen Algebra und die Zeigerdefinition sollen in kleinen Programmbibliotheken zusammengefasst werden. Alle Vorwärtsdeklarationen und eine Sammlung von Prototypen befinden sich im Header-File la_.h und sind im Wesentlichen selbstkommentierend. // la_.h /* Header-File fuer eine Bibliothek fuer Lineare Algebra */ #include <stdlib.h> #include <stdio.h> typedef double **laMatrix; void laGlSysEinlesen_(laMatrix *A, laMatrix *b); /* Liest die Matrix A und den Vektor b der rechten Seite aus der Datei "Matrix.in" bzw. Eingabedatei ... ein. Vektor = Spaltenvektor = n*1-Matrix */ void laGlSysAusgeben_(laMatrix A, laMatrix b); /* Gibt die Matrix A und den Vektor b auf dem Bildschirm aus. */ 45 void laMatAusgeben_(laMatrix A); /* Gibt die Matrix A auf dem Bildschirm aus. */ void laVektAusgeben_(laMatrix x); /* Gibt den reellen Vektor x (n*1-Matrix) auf dem Bildschirm aus. */ void laVektAusgebenI_(laMatrix x); /* Gibt den reellen Vektor x mit ganzzahligen Werten der Komponenten auf dem Bildschirm aus. */ laMatrix laMatrixNeu_(int n, int m); /* Erzeugt eine n*m-Matrix und speichert die Dimensionen in ihr ab. */ void laVernichte_(laMatrix A); /* Gibt den Speicher der Matrix A wieder frei. */ void laDim_(laMatrix A, int *n, int *m); /* Gibt die Dimensionen der Matrix A in n (Zeilen) und m (Spalten) zurueck. */ laMatrix laNull_(int n, int m); /* Erzeugt eine n*m-Nullmatrix */ laMatrix laEins_(int n, int m); /* Erzeugt eine n*m-Einheitsmatrix mit 1 auf der Diagonale */ void laMult_(laMatrix A, laMatrix B, laMatrix *C); /* Multipliziert die Matrix A mit der Matrix B algebraisch und speichert das Ergebnis in einer von der Funktion erzeugten Matrix ab. Auf diese Matrix zeigt nach dem Aufruf C. Fehlermeldung und Programmabbruch, falls die inneren Dimensionen von A und B nicht vertraeglich sind. */ void laAdd_(laMatrix A, laMatrix B, laMatrix *C); /* Addiert die Matrizen A und B und speichert das Ergebnis in einer von der Funktion erzeugten Matrix ab. Auf diese Matrix zeigt nach dem Aufruf C. Fehlermeldung und Programmabbruch, falls die Dimensionen von A und B nicht vertraeglich sind. */ void laSub_(laMatrix A, laMatrix B, laMatrix *C); /* Subtrahiert die Matrix B von der Matrix A und speichert das Ergebnis in einer von der Funktion erzeugten Matrix ab. Auf diese Matrix zeigt nach dem Aufruf C. Fehlermeldung und Programmabbruch, falls die Dimensionen von A und B nicht vertraeglich sind. */ void laSkMult_(double z, laMatrix A); /* Multipliziert die reelle Zahl z mit der Matrix A. */ laMatrix laTransponiere_(laMatrix A); /* Transponiert die Matrix A. Da die Matrix i. Allg. nicht quadratisch ist, muss auch der Speicher umorganisiert werden. A zeigt also nach dem Aufruf auf einen anderen Speicherbreich, der urspruengliche Speicherbereich wird freigegeben. */ void laVertauscheZe_(laMatrix A, int r, int s); /* Vertauscht in der Matrix A die Zeile r mit Zeile s. Fehlermeldung und Programmabbruch, falls r, s unzulaessig sind.*/ void laVertauscheSp_(laMatrix A, int r, int s); /* Vertauscht in der Matrix A die Spalte r mit Spalte s. Fehlermeldung und Programmabbruch, falls r, s unzulaessig sind.*/ void laKopiere_(laMatrix A, laMatrix B); /* Kopiert die Matrix A in eine vor der Funktion erzeugte Matrix. Auf diese Matrix zeigt nach dem Aufruf B. Dimensionskontrolle mit Fehlermeldung. */ void laKopiere1_(laMatrix A, laMatrix *B); /* Kopiert die Matrix A in eine von der Funktion erzeugte Matrix. Auf diese Matrix zeigt nach dem Aufruf B. */ void laElemMult_(laMatrix A, laMatrix B, laMatrix *C); /* Multiplikation der Matrizen A und B elementweise und Speichern des Ergebnisses in eine von der Funktion erzeugte Matrix. Auf diese Matrix zeigt nach dem Aufruf C. Fehlermeldung und Programmabbruch, falls die Dimensionen von A und B nicht vertraeglich sind. */ void laKreuzProd_(laMatrix A, laMatrix B, laMatrix *C); /* Berechnet das Kreuzprodukt der 3*1-Matrizen (Vektoren) A und B und speichert das Ergebnis in einer von der Funktion erzeugte 3*1-Matrix. Auf diese Matrix zeigt nach dem Aufruf C. Bei unzulaessigen Dimensionen von A, B Abbruch mit einer Fehlermeldung. */ 46 double laSkalProd_(laMatrix A, laMatrix B); /* Berechnet das Skalarprodukt der n*1-Matrizen (Vektoren) A und B und gibt den Wert zurueck. Bei unzulaessigen Dimensionen Abbruch mit Fehlermeldung. */ double laNorm_(char c, laMatrix A); /* Berechnet die Norm von n*m-Matrizen bzw. n*1-Matrizen (Vektoren) und gibt den Wert zurueck. Matrix Vektor Norm ’u’ : Zeilensummennorm, ’u’: Maximumnorm Norm ’1’ : Spaltensummennorm, ’1’: Betragssummennorm Norm ’F’ : Frobeniusnorm, ’2’: euklidische Norm Bei unzulaessiger Normwahl Fehlermeldung und Programmabbruch. */ Die Definitionen sind in verschiedenen Quelltexten untergebracht. Zunächst ist es das sehr wichtige Modul la_.c mit vier Grundfunktionen, wobei die Funktion laTransponiere_ hier keine Anwendung findet. // la_.c /* Eine Bibliothek fuer Lineare Algebra */ /* Definitionen von laMatrixNeu_, laVernichte_, laDim_, laTransponiere_ */ #include <stdlib.h> #include <stdio.h> #include "la_.h" laMatrix laMatrixNeu_(int n, int m) { unsigned i; laMatrix A; if((n<1)||(m<1)) { printf("Fehler: Dimension(en) unzulaessig\n"); getch(); exit(EXIT_FAILURE); } if((A = malloc(sizeof(double*)*n+2*sizeof(int)))==NULL) { fprintf(stderr,"Zu wenig Speicher\n"); getch(); exit(EXIT_FAILURE); } *((int*)A) = n; *(((int*)A)+1) = m; A = (double**)(((int*)A)+2); for(i=0;i<n;i++) if((*(A+i) = malloc(sizeof(double)*m))==NULL) { fprintf(stderr,"Zu wenig Speicher\n"); getch(); exit(EXIT_FAILURE); } return(A); } /*----------------------------------------------------------------------*/ void laVernichte_(laMatrix A) { unsigned i,n; n = *(((int*)A)-2); for(i=0;i<n;i++) free(*(A+i)); free(((int*)A)-2); } /*----------------------------------------------------------------------*/ void laDim_(laMatrix A, int *n, int *m) { *n = *(((int*)A)-2); *m = *(((int*)A)-1); } 47 /*----------------------------------------------------------------------*/ laMatrix laTransponiere_(laMatrix A) { int n,m,i,j; laMatrix B; laDim_(A,&n,&m); B = laMatrixNeu_(m,n); for(i=0;i<=n;++i) for(j=0;j<=m;++j) B[j][i] = A[i][j]; laVernichte_(A); return(B); } Die Funktionen für die Ein- und Ausgabe sind problemangepasst und im Modul laInOut_.c zusammengefasst. Sie werden alle genutzt. Dort aufgerufen werden auch die Funktionen laMatrixNeu_, laDim_. // laInOut_.c /* Eine Bibliothek fuer Lineare Algebra, Ein/Ausgaben */ /* Definitionen von laGlSysEinlesen_, laGlSysAusgeben_, laMatAusgeben_, laVektAusgeben_, laVektAusgebenI_ */ #include <stdlib.h> #include <stdio.h> //#include <conio.h> #include "la_.h" /*----------------------------------------------------------------------------*/ void laGlSysEinlesen_(laMatrix *A, laMatrix *b) { FILE *matIn; int i,j,n,m; char s[80]="Matrix.in"; // Initialisierung printf("Dateiname fuer Daten A,b zum LGS Ax=b : "); scanf("%s",&s); // auch scanf("%s",s); matIn = fopen(s,"r"); // matIn = fopen("Matrix.in","r"); if(matIn==NULL) { printf("Fehler: Datei nicht gefunden\n"); getch(); exit(EXIT_FAILURE); } fscanf(matIn,"%u %u",&n,&m); *A = laMatrixNeu_(n,m); *b = laMatrixNeu_(n,1); for(i=0;i<n;i++) for(j=0;j<m;j++) fscanf(matIn,"%lf",&(*A)[i][j]); for(i=0;i<n;i++) fscanf(matIn,"%lf",&(*b)[i][0]); fclose(matIn); } /*----------------------------------------------------------------------------*/ void laGlSysAusgeben_(laMatrix A, laMatrix b) { int i,j,n,m; laDim_(A,&n,&m); for(i=0;i<n;i++) { for(j=0;j<m;j++) printf(" %10.6lf ",A[i][j]); printf(" | %10.6lf \n",b[i][0]); } } 48 void laMatAusgeben_(laMatrix A) { int i,j,n,m; laDim_(A,&n,&m); for(i=0;i<n;i++) { for(j=0;j<m;j++) printf(" %10.6lf ",A[i][j]); printf("\n"); } } /*----------------------------------------------------------------------------*/ void laVektAusgeben_(laMatrix x) { int i,n,m; laDim_(x,&n,&m); for(i=0;i<n;i++) printf(" %10.6lf ",x[i][0]); printf("\n"); } /*----------------------------------------------------------------------------*/ void laVektAusgebenI_(laMatrix x) { int i,n,m; laDim_(x,&n,&m); for(i=0;i<n;i++) printf(" %2.0lf ",x[i][0]); printf("\n"); } Die Beispiele werden als ASCII-Dateien in der Datenreihenfolge n, m, A(1..n, 1..m), b(1..n), ... bereitgestellt. Der Dateiname für die Daten A, b zum LGS wird eingelesen. Als Testvarianten zeigen wir LGS mit regulären und singulären Koeffizientenmatrizen (reg.: matrix23, matrix3, matrix40, sing.: matrix57, matrix58). matrix23 -------3 3 1 -2 2 -1 1 -1 -2 -2 1 1 -1 -3 Loesung: 1 1 1 EW: 1,1,1 matrix3 ------------------2 2 1.441969 1.040807 1.040807 0.751250 0.401162 0.289557 Loesung: 1 -1 EW: 2.193218999999544 4.5595081932092e-13 matrix40 ---------------5 5 2 -3 1 -2 4 -4 6 -2 5 -6 6 -9 3 -4 10 2 -4 3 2 -3 -2 5 -3 2 -1 7 -10 17 5 1 Loesung: 14,14,13,0,2 EW: 0.914 5.5713+-i*8.844 -0.0285+-i*0.489 matrix57 -----------3 3 1 -2 2 -1 2 -1 -2 4 1 1 0 3 Loesung: 1 1 1 EW: 0,2+-i*2.646 matrix58 -------3 3 2 2 2 1 3 0 2 4 1 5 4 7 Loesung: keine EW: 0,1,5 Von den anderen selbständigen Definitionen benötigen wir eigentlich nur die Funktionen laKopiere_, laVertauscheZe_ und wegen einiger Kontrollrechnungen auch laKopiere1_, laAdd_, laNorm_. Aber es sollen jedoch alle zum Projekt hinzugefügt werden. 49 // laKopiere_.c, B = A #include "la_.h" void laKopiere_(laMatrix A, laMatrix B) { int n,m,r,s,i,j; laDim_(A,&n,&m); laDim_(B,&r,&s); if((n!=r)||(m!=s)) { printf("Fehler: Matrizen sind nicht von gleicher Dimension\n"); getch(); exit(EXIT_FAILURE); } for(i=0;i<n;i++) for(j=0;j<m;j++) B[i][j] = A[i][j]; } // laVertauscheZe_.c, #include "la_.h" Zeilenvertauschung in Matrix, Zeilen r<->s void laVertauscheZe_(laMatrix A, int r, int s) { int n,m,j; double h; laDim_(A,&n,&m); if((r<0)||(r>n-1)||(s<0)||(s>n-1)) { printf("Fehler: Zeilennummer(n) unzulaessig\n"); getch(); exit(EXIT_FAILURE); } for(j=0;j<m;j++) { h = A[s][j]; A[s][j] = A[r][j]; A[r][j] = h; } } --------------------------------------------------------------------// laKopiere1_.c, B = A #include "la_.h" void laKopiere1_(laMatrix A, laMatrix *B) { int n,m,i,j; laDim_(A,&n,&m); *B = laMatrixNeu_(n,m); for(i=0;i<n;i++) for(j=0;j<m;j++) (*B)[i][j] = A[i][j]; } // laAdd_.c, C = A+B #include "la_.h" void laAdd_(laMatrix A, laMatrix B, laMatrix *C) { int n,m,k,l,i,j; laDim_(A,&n,&m); laDim_(B,&k,&l); if((n!=k)||(m!=l)) { printf("Fehler: Matrixdimension(en) nicht gleich\n"); getch(); exit(EXIT_FAILURE); } *C = laMatrixNeu_(n,m); for(i=0;i<n;i++) 50 for(j=0;j<m;j++) (*C)[i][j] = A[i][j]+B[i][j]; } // laNorm_.c, Berechnet die Norm von n*m-Matrizen bzw. n*1-Matrizen (Vektoren) #include <math.h> #include "la_.h" double laNorm_(char cc, laMatrix A) { int n,m,i,j; double no,h; no = 0; if((cc!=’u’)&&(cc!=’1’)&&(cc!=’2’)&&(cc!=’F’)) { printf("Fehler: Falsche Norm\n"); getch(); exit(EXIT_FAILURE); } laDim_(A,&n,&m); // Fallunterscheidung m=1 und m>1 if(m==1) switch (cc) { case ’u’: for(i=0;i<n;i++) if (fabs(A[i][0])>no) no = fabs(A[i][0]); break; case ’1’: for(i=0;i<n;i++) no += fabs(A[i][0]); break; case ’2’: for(i=0;i<n;i++) no += A[i][0]*A[i][0]; no = sqrt(no); break; } else switch (cc) { case ’u’: for(i=0;i<n;i++) { h = 0; for(j=0;j<m;j++) h += fabs(A[i][j]); if (h>no) no = h; } break; case ’1’: for(j=0;j<m;j++) { h = 0; for(i=0;i<n;i++) h += fabs(A[i][j]); if (h>no) no = h; } break; case ’F’: for(i=0;i<n;i++) for(j=0;j<m;j++) no += A[i][j]*A[i][j]; no = sqrt(no); break; } return(no); } --------------------------------------------------------------------// laEins_.c, Erzeugung der Einheitsmatrix #include "la_.h" laMatrix laEins_(int n, int m) { int i,min; laMatrix M; 51 M = laNull_(n,m); if(n>m) min = m; else min = n; for (i=0;i<min;i++) M[i][i] = 1; return M; } // LaElemMult_.c, #include "la_.h" C = A*B elementweise void laElemMult_(laMatrix A, laMatrix B, laMatrix *C) { int n,m,k,l,i,j; laDim_(A,&n,&m); laDim_(B,&k,&l); if((n!=k)||(m!=l)) { printf("Fehler: Matrizen sind nicht von gleicher Dimension\n"); getch(); exit(EXIT_FAILURE); } *C = laMatrixNeu_(n,m); for(i=0;i<n;i++) for(j=0;j<m;j++) (*C)[i][j] = A[i][j]*B[i][j]; } // laKreuzProd_.c, #include "la_.h" Kreuzprodukt zweier 3*1-Matrizen (3-dimensionale Vektoren) void laKreuzProd_(laMatrix A, laMatrix B, laMatrix *C) { int na,ma,nb,mb; if((A==*C)||(B==*C)) { fprintf(stderr,"Fehler: Gleiche Zeiger\n"); getch(); exit(EXIT_FAILURE); } laDim_(A,&na,&ma); laDim_(B,&nb,&mb); if((na!=3)||(ma!=1)||(nb!=3)||(mb!=1)) { fprintf(stderr,"In A,B Zeilenanzahl<>3, Spaltenanzahl<>1\n"); exit(EXIT_FAILURE); } *C = laMatrixNeu_(3,1); (*C)[0][0] = A[1][0]*B[2][0]-A[2][0]*B[1][0]; (*C)[1][0] =-A[0][0]*B[2][0]+A[2][0]*B[0][0]; (*C)[2][0] = A[0][0]*B[1][0]-A[1][0]*B[0][0]; } // laMult_.c, C = AB algebraisch #include "la_.h" void laMult_(laMatrix A, laMatrix B, laMatrix *C) { int i,j,k,n,m,r,s; double h; laDim_(A, &n, &m); laDim_(B, &r, &s); if(m!=r) { printf ("Fehler: innere Dimensionen unzulaessig\n"); getch(); exit(EXIT_FAILURE); } *C = laMatrixNeu_(n,s); for(i=0;i<n; i++) for(j=0;j<s;j++) { 52 h = 0; for(k=0;k<m;k++) h += A[i][k]*B[k][j]; (*C)[i][j] = h; } } // laNull_.c, Erzeugung der Nullmatrix # include "la_.h" laMatrix laNull_(int n, int m) { int i,j; laMatrix M; if((n<1)||(m<1)) { printf("Fehler: Dimension(en) unzulaessig\n"); getch(); exit(EXIT_FAILURE); } M = laMatrixNeu_(n,m); for (i=0;i<n;i++) for (j=0;j<m;j++) M[i][j] = 0; return M; } // laSkalProd_.c, #include "la_.h" Skalarprodukt der n*1-Matrizen (Vektoren) A und B double laSkalProd_(laMatrix A, laMatrix B) { int n,m,k,l,i; double erg; laDim_(A,&n,&m); laDim_(B,&k,&l); if((n==k)&&(m==1)&&(l==1)) { erg=0; for(i=0;i<n;i++) erg += A[i][0]*B[i][0]; return (erg); } printf("Fehler: Falsche Dimension(en)\n"); getch(); exit(EXIT_FAILURE); } // laSkMult_.c, #include "la_.h" Multiplikation der Zahl z mit der Matrix A void laSkMult_(double z, laMatrix A) { int n,m,i,j; laDim_(A,&n,&m); for(i=0;i<n;i++) for(j=0;j<m;j++) A[i][j] = A[i][j]*z; } // laSub_.c, C = A-B #include "la_.h" void laSub_(laMatrix A, laMatrix B, laMatrix *C) { int n,m,k,l,i,j; laDim_(A,&n,&m); laDim_(B,&k,&l); if((n!=k)||(m!=l)) { 53 printf("Fehler: Matrixdimension(en) nicht gleich\n"); getch(); exit(EXIT_FAILURE); } *C = laMatrixNeu_(n,m); for(i=0;i<n;i++) for(j=0;j<m;j++) (*C)[i][j] = A[i][j]-B[i][j]; } // laVertauscheSp_.c, #include "la_.h" Spaltenvertauschung in Matrix, Spalten r<->s void laVertauscheSp_(laMatrix A, int r, int s) { int n,m,i; double h; laDim_(A,&n,&m); if((r<0)||(r>m-1)||(s<0)||(s>m-1)) { printf("Fehler: Spaltennummer(n) unzulaessig\n"); getch(); exit(EXIT_FAILURE); } for(i=0;i<n;i++) { h = A[i][r]; A[i][r] = A[i][s]; A[i][s] = h; } } Nun fehlt nur noch der Quelltext (Rahmenprogramm) meinprog_.c mit der main-Funktion. In ihm befinden sich die notwendigen include-Anweisungen, die Funktion gauss4c, die Eingaben, Berechnungen, Ausgaben sowie gewisse Kontrollrechnungen zwecks Überprufung der Richtigkeit einiger Funktionen. // meinprog_.c #include<stdlib.h> #include<stdio.h> #include<math.h> //#include<conio.h> #include "la_.h" void gauss4c(int n, int *t, laMatrix *A, laMatrix b, laMatrix *x, laMatrix *perm, int *sing) ... int main() { int n,m,t,sing; double erg; laMatrix A,b,x,LU,c,perm,C1,C2,C3; char kont,prot; printf("Resttableau-Gauss-Algorithmus mit Spaltenpivotisierung und Zeilenvertauschung\n"); printf("Tausch mit betragsgroessten NNE in der Spalte\n"); printf("Ax=b, A => L\\U, P, PA=LU \n"); printf("Ux=c Loesung mit Rueckwaertseinsetzen\n"); printf("-----------------------------------------------------------------------------\n\n"); laGlSysEinlesen_(&A,&b); printf("Kontrollausgabe A,b (j/n)? "); kont = getch(); printf("\n"); if(kont==’j’) { laDim_(A,&n,&m); printf("\nDimension A(n,m): n=%i, m=%i\n",n,m); printf("\nA | b\n"); laGlSysAusgeben_(A,b); printf("-----------------------------------------------------------------------------\n\n"); getch(); } 54 laDim_(A,&n,&m); if(n!=m) { printf("\nDimension n<>m, keine Berechnung"); getch(); return (-1); } LU = laMatrixNeu_(n,m); laKopiere_(A,LU); c = laMatrixNeu_(n,1); laKopiere_(b,c); x = laMatrixNeu_(n,1); perm = laMatrixNeu_(n,1); gauss4c(n,&t,&LU,c,&x,&perm,&sing); if(sing==0) { printf("Loesungsvektor x\n"); laVektAusgeben_(x); printf("-----------------------------------------------------------------------------\n"); getch(); } printf("\nErgebnisprotokoll (j/n)? "); prot = getch(); if(prot==’j’) { printf("\n---------------------------------------------------------------------------\n"); if (sing==1) printf("Achtung! Vorzeitiger Abbruch im Schritt t = %i\n",t); printf("Dimension A(n,m): n=%i, m=%i\n",n,m); printf("\nA | b\n"); laGlSysAusgeben_(A,b); if(sing==0) { printf("\nLoesungsvektor x\n"); laVektAusgeben_(x); } printf("\nPermutationsvektor perm\n"); laVektAusgebenI_(perm); printf("\nZerlegung PA=L\\U\n"); laMatAusgeben_(LU); printf("-----------------------------------------------------------------------------\n"); getch(); } printf("\n\nKontrollrechnungen (j/n)? "); kont = getch(); if(kont==’j’) { printf("\nC1 = LU mittels laKopiere_\n"); C1 = laMatrixNeu_(n,m); laKopiere_(LU,C1); laMatAusgeben_(C1); printf("\nC2 = LU mittels laKopiere1_\n"); laKopiere1_(LU,&C2); laMatAusgeben_(C2); printf("\nC3 = A+C2\n"); laAdd_(A,C2,&C3); laMatAusgeben_(C3); printf("\nNormen von b\n"); laVektAusgeben_(b); printf("||b||_u=%10.6lf, ||b||_1=%10.6lf, ||b||_2=%10.6lf\n",laNorm_(’u’,b),laNorm_(’1’,b),laNorm_(’2’,b)); printf("\nNormen von A\n"); laMatAusgeben_(A); printf("||A||_u=%10.6lf, ||A||_1=%10.6lf, ||A||_F=%10.6lf\n",laNorm_(’u’,A),laNorm_(’1’,A),laNorm_(’F’,A)); getch(); laVernichte_(C1); laVernichte_(C2); laVernichte_(C3); } // Speicherplatz freigeben laVernichte_(A); laVernichte_(LU); laVernichte_(b); laVernichte_(c); laVernichte_(x); laVernichte_(perm); } 55 Zusammenstellung des C-Projekts gauss4c im Arbeitsverzeichnis C:\d\Neundorf\nwptexte\tech phy\05\dev cpp\projekt4 Im Projektfenster steht der Baum des Projekts. Die wirklich verwendeten Module sind fett hervorgehoben. S 2- –ℵ≡ gauss4c ||− la .c | laAdd .c |− | laEins_.c |− | |− laElemMult_.c | |− laInOut .c | |− laKopiere .c | laKopiere1 .c |− | laKreuzProd_.c |− | |− laMult_.c | laNorm .c |− | |− laNull_.c | laSkalProd_.c |− | |− laSkMult_.c | |− laSub_.c | laVertauscheSp_.c |− | |− laVertauscheZe .c | meinprog .c − Klickt man den Schalter Klassen an, so erscheint im Projektfenster eine umfangreiche Liste von Klassen und Funktionen zum Projekt. Man klickt nun auf das Projekt gauss4c und kommt zum • Menüpunkt Ausführen ⇒ Kompilieren u. Ausführen Es folgen Informationen zum Übersetzen und im Kompilier Log steht Building Makefile: "C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\projekt4\Makefile.win" Fuehrt make... aus make.exe -f "C:\d\Neundorf\nwptexte\tech_phy\05\dev_cpp\projekt4\Makefile.win" all gcc.exe -c la_.c -o la_.o -I"C:/Dev-Cpp/include" -I"C:/d/Neundorf/nwptexte/tech_phy/05/dev_cpp/projekt4" gcc.exe -c laAdd_.c -o laAdd_.o -I"C:/Dev-Cpp/include" -I"C:/d/Neundorf/nwptexte/tech_phy/05/dev_cpp/projekt4" gcc.exe -c laEins_.c -o laEins_.o -I"C:/Dev-Cpp/include" -I"C:/d/Neundorf/nwptexte/tech_phy/05/dev_cpp/projekt4" gcc.exe -c laElemMult_.c -o laElemMult_.o -I"C:/Dev-Cpp/include" -I"C:/d/Neundorf/nwptexte/tech_phy/05/dev_cpp/projekt4" gcc.exe -c laInOut_.c -o laInOut_.o -I"C:/Dev-Cpp/include" -I"C:/d/Neundorf/nwptexte/tech_phy/05/dev_cpp/projekt4" gcc.exe -c laKopiere_.c -o laKopiere_.o -I"C:/Dev-Cpp/include" -I"C:/d/Neundorf/nwptexte/tech_phy/05/dev_cpp/projekt4" gcc.exe -c laKopiere1_.c -o laKopiere1_.o -I"C:/Dev-Cpp/include" -I"C:/d/Neundorf/nwptexte/tech_phy/05/dev_cpp/projekt4" gcc.exe -c laKreuzProd_.c -o laKreuzProd_.o -I"C:/Dev-Cpp/include" -I"C:/d/Neundorf/nwptexte/tech_phy/05/dev_cpp/projekt4" gcc.exe -c laMult_.c -o laMult_.o -I"C:/Dev-Cpp/include" -I"C:/d/Neundorf/nwptexte/tech_phy/05/dev_cpp/projekt4" gcc.exe -c laNorm_.c -o laNorm_.o 56 -s -s -s -s -s -s -s -s -s -I"C:/Dev-Cpp/include" -I"C:/d/Neundorf/nwptexte/tech_phy/05/dev_cpp/projekt4" gcc.exe -c laNull_.c -o laNull_.o -I"C:/Dev-Cpp/include" -I"C:/d/Neundorf/nwptexte/tech_phy/05/dev_cpp/projekt4" gcc.exe -c laSkalProd_.c -o laSkalProd_.o -I"C:/Dev-Cpp/include" -I"C:/d/Neundorf/nwptexte/tech_phy/05/dev_cpp/projekt4" gcc.exe -c laSkMult_.c -o laSkMult_.o -I"C:/Dev-Cpp/include" -I"C:/d/Neundorf/nwptexte/tech_phy/05/dev_cpp/projekt4" gcc.exe -c laSub_.c -o laSub_.o -I"C:/Dev-Cpp/include" -I"C:/d/Neundorf/nwptexte/tech_phy/05/dev_cpp/projekt4" gcc.exe -c laVertauscheSp_.c -o laVertauscheSp_.o -I"C:/Dev-Cpp/include" -I"C:/d/Neundorf/nwptexte/tech_phy/05/dev_cpp/projekt4" gcc.exe -c laVertauscheZe_.c -o laVertauscheZe_.o -I"C:/Dev-Cpp/include" -I"C:/d/Neundorf/nwptexte/tech_phy/05/dev_cpp/projekt4" gcc.exe -c meinprog_.c -o meinprog_.o -I"C:/Dev-Cpp/include" -I"C:/d/Neundorf/nwptexte/tech_phy/05/dev_cpp/projekt4" -s -s -s -s -s -s -s -s gcc.exe la_.o laAdd_.o laEins_.o laElemMult_.o laInOut_.o laKopiere_.o laKopiere1_.o laKreuzProd_.o laMult_.o laNorm_.o laNull_.o laSkalProd_.o laSkMult_.o laSub_.o laVertauscheSp_.o laVertauscheZe_.o meinprog_.o -o "gauss4c.exe" -L"C:/Dev-Cpp/lib" -I"C:/Dev-Cpp/include" -I"C:/d/Neundorf/nwptexte/tech_phy/05/dev_cpp/projekt4" -s Ausfuehrung beendet Kompilierung erfolgreich Im Verzeichnis stehen dann die Projektdatei gauss4c.dev, die ausführbare Datei gauss4c.exe sowie alle Objektdateien *.o der Projektquellen. Dazu kommt zum Projekt noch das sogenannte Makefile Makefile.win. # Project: gauss4c # Makefile created by Dev-C++ 4.9.6.0 CC = gcc.exe WINDRES = windres.exe RES = OBJ = la_.o laAdd_.o laEins_.o laElemMult_.o laInOut_.o laKopiere_.o laKopiere1_.o laKreuzProd_.o laMult_.o laNorm_.o laNull_.o laSkalProd_.o laSkMult_.o laSub_.o laVertauscheSp_.o laVertauscheZe_.o meinprog_.o $(RES) LIBS = -L"C:/Dev-Cpp/lib" INCS = -I"C:/Dev-Cpp/include" BIN = gauss4c.exe CFLAGS = $(INCS) -s .PHONY: all all-before all-after clean clean-custom all: all-before gauss4c.exe all-after clean: clean-custom rm -f $(OBJ) $(BIN) $(BIN): $(OBJ) $(CC) $(OBJ) -o "gauss4c.exe" $(LIBS) $(CFLAGS) la_.o: la_.c $(CC) -c la_.c -o la_.o $(CFLAGS) laAdd_.o: laAdd_.c $(CC) -c laAdd_.c -o laAdd_.o $(CFLAGS) laEins_.o: laEins_.c $(CC) -c laEins_.c -o laEins_.o $(CFLAGS) laElemMult_.o: laElemMult_.c $(CC) -c laElemMult_.c -o laElemMult_.o $(CFLAGS) laInOut_.o: laInOut_.c $(CC) -c laInOut_.c -o laInOut_.o $(CFLAGS) laKopiere_.o: laKopiere_.c $(CC) -c laKopiere_.c -o laKopiere_.o $(CFLAGS) laKopiere1_.o: laKopiere1_.c $(CC) -c laKopiere1_.c -o laKopiere1_.o $(CFLAGS) laKreuzProd_.o: laKreuzProd_.c $(CC) -c laKreuzProd_.c -o laKreuzProd_.o $(CFLAGS) laMult_.o: laMult_.c $(CC) -c laMult_.c -o laMult_.o $(CFLAGS) laNorm_.o: laNorm_.c $(CC) -c laNorm_.c -o laNorm_.o $(CFLAGS) laNull_.o: laNull_.c 57 $(CC) -c laNull_.c -o laNull_.o $(CFLAGS) laSkalProd_.o: laSkalProd_.c $(CC) -c laSkalProd_.c -o laSkalProd_.o $(CFLAGS) laSkMult_.o: laSkMult_.c $(CC) -c laSkMult_.c -o laSkMult_.o $(CFLAGS) laSub_.o: laSub_.c $(CC) -c laSub_.c -o laSub_.o $(CFLAGS) laVertauscheSp_.o: laVertauscheSp_.c $(CC) -c laVertauscheSp_.c -o laVertauscheSp_.o $(CFLAGS) laVertauscheZe_.o: laVertauscheZe_.c $(CC) -c laVertauscheZe_.c -o laVertauscheZe_.o $(CFLAGS) meinprog_.o: meinprog_.c $(CC) -c meinprog_.c -o meinprog_.o $(CFLAGS) Obwohl die Bibliotheken Dev-Cpp/lib und Dev-Cpp/include mit ihren zahlreichen HeaderFiles ins Projekt eingebunden sind, ist es notwendig, in den entsprechenden Modulen noch die Include-Anweisungen zu belassen, also #include<stdlib.h> #include<stdio.h> #include<math.h> // fuer // fuer // fuer EXIT_FAILURE stdin, stderr, ... mathematische Standardfunktionen Beispiel 1 Ax = b, A regulär 1 1 1 −2 2 ∗ −1 1 , −1 , eindeutige Lösung x = A b = −1 1 −1 , b = A= 1 −3 −2 −2 1 0 0 1 p = (3, 1, 2), P = 1 0 0 (Permutationsmatrix), 0 1 0 1 0 0 −2 −2 1 −2 −2 1 5 2 = − 12 1 0 0 −3 P A = 1 −2 = LU. 2 1 1 2 −1 1 −1 0 0 −3 1 2 6 Eingabe- und Ergebnisfenster Haltepunkte wie getch() sind bei der Ausgabe großer Datenmengen als auch nach Fehlermeldungen sinvoll. 2 C:\d\Neundorf\nwptexte\tech phy\05\dev cpp\projekt4\gauss4c.exe Resttableau-Gauss-Algorithmus mit Spaltenpivotisierung und Zeilenvertauschung Tausch mit betragsgroessten NNE in der Spalte Ax=b, A => L\U, P, PA=LU Ux=c Loesung mit Rueckwaertseinsetzen ———————————————————————————————————— Dateiname fuer Daten A,b zum LGS Ax=b : Matrix23 Kontrollausgabe A,b (j/n)? Dimension A(n,m): n=3, m=3 A| b 1.000000 -2.000000 2.000000 | 1.000000 -1.000000 1.000000 -1.000000 | -1.000000 -2.000000 -2.000000 1.000000 | -3.000000 ———————————————————————————————————— Loesungsvektor x 1.000000 1.000000 1.000000 58 Fortsetzung 2 C:\d\Neundorf\nwptexte\tech phy\05\dev cpp\projekt4\gauss4c.exe Ergebnisprotokoll (j/n)? ———————————————————————————————————— Dimension A(n,m): n=3, m=3 A| b 1.000000 -2.000000 2.000000 | 1.000000 -1.000000 1.000000 -1.000000 | -1.000000 -2.000000 -2.000000 1.000000 | -3.000000 Loesungsvektor x 1.000000 1.000000 1.000000 Permutationsvektor perm 3 1 2 Zerlegung PA=L\U -2.000000 -2.000000 1.000000 -0.500000 -3.000000 2.500000 0.500000 -0.666667 0.166667 ———————————————————————————————————— Kontrollrechnungen (j/n)? C1 = LU mittels laKopiere -2.000000 -2.000000 1.000000 -0.500000 -3.000000 2.500000 0.500000 -0.666667 0.166667 C2 = LU mittels laKopiere1 -2.000000 -2.000000 1.000000 -0.500000 -3.000000 2.500000 0.500000 -0.666667 0.166667 C3 = A+C2 -1.000000 -4.000000 -1.500000 -2.000000 -1.500000 -2.666667 3.000000 1.500000 1.166667 Normen von b 1.000000 -1.000000 -3.000000 ||b|| u= 3.000000, ||b|| 1= 5.000000, ||b|| 2= 3.316625 Normen von A 1.000000 -2.000000 2.000000 -1.000000 1.000000 -1.000000 -2.000000 -2.000000 1.000000 ||A|| u= 5.000000, ||A|| 1= 5.000000, ||A|| F= 4.582576 Für die Ergebnisdarstellung bei größeren LGS bzw. mit mehr Mantissenstellen der Dezimalzahlen muss man geeignete Veränderungen an der Ein- und Ausgabefunktionen vornehmen. 59 Beispiel 2 Ax = b, A regulär, aber det(A) = 10−12 und damit sehr schlechte Kondition der Systemmatrix 1 0.401162 1.441969 1.040807 ∗ −1 , , Lösung x = A b = , b= A= −1 0.289557 1.040807 0.751250 1 0 (Permutationsmatrix), p = (1, 2), P = 0 1 ! ! ! 1441969 1040807 1040807 1441969 1 0 1000000 1000000 1000000 1000000 PA = A = = = 1 751250 1040807 1040807 0 1 1000000 1000000 14419691 1441969000000 ! ! 1 0 1.441969 1.040807 = = LU. 0.7217956835410470 1 0 0.693496 10−12 Eingabe- und Ergebnisfenster 2 C:\d\Neundorf\nwptexte\tech phy\05\dev cpp\projekt4\gauss4c.exe Resttableau-Gauss-Algorithmus mit Spaltenpivotisierung und Zeilenvertauschung Tausch mit betragsgroessten NNE in der Spalte Ax=b, A => L\U, P, PA=LU Ux=c Loesung mit Rueckwaertseinsetzen ———————————————————————————————————— Dateiname fuer Daten A,b zum LGS Ax=b : Matrix3 Kontrollausgabe A,b (j/n)? Dimension A(n,m): n=2, m=2 A| b 1.441969 1.040807 | 0.401162 1.040807 0.751250 | 0.289557 ———————————————————————————————————— Loesungsvektor x 1.000058 -1.000080 ———————————————————————————————————— Ergebnisprotokoll (j/n)? ———————————————————————————————————— Dimension A(n,m): n=2, m=2 A| b 1.441969 1.040807 1.040807 | 0.401162 0.751250 | 0.289557 Loesungsvektor x 1.000058 -1.000080 Permutationsvektor perm 1 2 Zerlegung PA=L\U 1.441969 1.040807 0.721796 0.000000 ———————————————————————————————————— 60 Fortsetzung 2 C:\d\Neundorf\nwptexte\tech phy\05\dev cpp\projekt4\gauss4c.exe Kontrollrechnungen (j/n)? C1 = LU mittels laKopiere 1.441969 1.040807 0.721796 0.000000 C2 = LU mittels laKopiere1 1.441969 1.040807 0.721796 0.000000 C3 = A+C2 2.883938 2.081614 1.762603 0.751250 Normen von b 0.401162 0.289557 ||b|| 1= 0.690719, ||b|| u= 0.401162, ||b|| 2= 0.494747 Normen von A 1.441969 1.040807 1.040807 0.751250 ||A|| u= 2.482776, ||A|| 1= 2.482776, ||A|| F= 2.193219 Wählt man im Gauß-Algorithmus gauss4c() beim Test auf die Singularität der Matrix A die Toleranz zu grob, z. B. ε ≥ 10−12 , dann wird wegen u22 = 0.693496 10−12 < ε die Matrix als singulär eingestuft und das Programm abgebrochen. Ein Weiterrechnen in den Resttableaus wäre bei zusätzlicher Spaltenvertauschung durchaus möglich. Dabei könnte man dann auch den Matrixrang ermitteln. Aber die Singularität als solche bleibt natürlich bestehen. 2 C:\d\Neundorf\nwptexte\tech phy\05\dev cpp\projekt4\gauss4c.exe Resttableau-Gauss-Algorithmus mit Spaltenpivotisierung und Zeilenvertauschung Tausch mit betragsgroessten NNE in der Spalte Ax=b, A => L\U, P, PA=LU Ux=c Loesung mit Rueckwaertseinsetzen ———————————————————————————————————— Dateiname fuer Daten A,b zum LGS Ax=b : Matrix3 Kontrollausgabe A,b (j/n)? Dimension A(n,m): n=2, m=2 A| b 1.441969 1.040807 | 0.401162 1.040807 0.751250 | 0.289557 ———————————————————————————————————— Matrix singulaer, Abbruch im Schritt = 2 Ergebnisprotokoll (j/n)? ———————————————————————————————————— Achtung! Vorzeitiger Abbruch im Schritt t = 2 ... 61 Beispiel 3 Ax = b, A regulär 2 −3 1 −2 4 −4 6 −2 5 −6 6 −9 3 −4 10 A= 2 −4 3 2 −3 −2 5 −3 2 −1 0 0 0 0 p = (3, 5, 4, 2, 1), P = 0 0 0 1 1 0 6 −9 2 −3 , b = 1 0 0 0 0 0 0 1 0 0 0 1 0 0 0 1 0 2 −1 − 31 1 2 −3 = 3 2 5 −6 − 3 3 −4 5 −3 −2 3 P A = 2 −4 6 −2 −4 1 −2 10 4 1 3 7 −10 17 5 1 ∗ −1 , Lösung x = A b = (Permutationsmatrix), 0 0 0 1 0 0 − 12 1 0 0 0 1 0 − 27 0 0 0 0 0 0 0 Eingabe- und Ergebnisfenster (Auswahl) 1 6 −9 0 3 −4 10 7 3 − 31 6 2 3 6 7 0 1 0 0 2 3 11 3 7 3 0 0 0 2 −2 2 C:\d\Neundorf\nwptexte\tech phy\05\dev cpp\projekt4\gauss4c.exe Resttableau-Gauss-Algorithmus mit Spaltenpivotisierung und Zeilenvertauschung Tausch mit betragsgroessten NNE in der Spalte Ax=b, A => L\U, P, PA=LU Ux=c Loesung mit Rueckwaertseinsetzen ———————————————————————————————————— Dateiname fuer Daten A,b zum LGS Ax=b : Matrix40 Kontrollausgabe A,b (j/n)? Dimension A(n,m): n=5, m=5 A| b 2.000000 -3.000000 1.000000 -2.000000 4.000000 | 7.000000 -4.000000 6.000000 -2.000000 5.000000 -6.000000 | -10.000000 6.000000 -9.000000 3.000000 -4.000000 10.000000 | 17.000000 2.000000 -4.000000 3.000000 2.000000 -3.000000 | 5.000000 -2.000000 5.000000 3.000000 -3.000000 2.000000 | 1.000000 ———————————————————————————————————— Loesungsvektor x 14.000000 14.000000 13.000000 0.000000 2.000000 ———————————————————————————————————— ... Permutationsvektor perm 3 5 4 2 1 Zerlegung PA=L\U 6.000000 -9.000000 3.000000 -4.000000 10.000000 -0.333333 2.000000 -2.000000 0.666667 2.333333 0.333333 -0.500000 1.000000 3.666667 -5.166667 -0.666667 0.000000 0.000000 2.333333 0.666667 0.333333 0.000000 0.000000 -0.285714 0.857143 62 14 14 13 0 2 , = LU. Beispiel 4 Ax = b, A singulär, rang(A) = 2 1 1 −2 2 0 , −1 2 −1 , b = A= 3 −2 4 1 1 ∗ 1 , eine der Lösungen ist x = 1 2c − 1 , c reell. c die einparametrige Lösungsmenge ist x∗ = 1 Abbruch des Gauß-Algorithmus im zweiten Schritt, bis dahin wird folgender “Zwischenstand“ erreicht. 0 0 1 p = (3, 2, 1), P = 0 1 0 , 1 0 0 L= 1 0 0 1 2 − 12 ∗ 0 , U = ∗ ∗ −2 4 0 0 0 0 Eingabe- und Ergebnisfenster 1 − 32 . 5 2 2 C:\d\Neundorf\nwptexte\tech phy\05\dev cpp\projekt4\gauss4c.exe Resttableau-Gauss-Algorithmus mit Spaltenpivotisierung und Zeilenvertauschung Tausch mit betragsgroessten NNE in der Spalte Ax=b, A => L\U, P, PA=LU Ux=c Loesung mit Rueckwaertseinsetzen ———————————————————————————————————— Dateiname fuer Daten A,b zum LGS Ax=b : Matrix57 Kontrollausgabe A,b (j/n)? Dimension A(n,m): n=3, m=3 A| b 1.000000 -2.000000 2.000000 | 1.000000 -1.000000 2.000000 -1.000000 | 0.000000 -2.000000 4.000000 1.000000 | 3.000000 ———————————————————————————————————— Matrix singulaer, Abbruch im Schritt = 2 63 Fortsetzung 2 C:\d\Neundorf\nwptexte\tech phy\05\dev cpp\projekt4\gauss4c.exe Ergebnisprotokoll (j/n)? ———————————————————————————————————— Achtung! Vorzeitiger Abbruch im Schritt t = 2 Dimension A(n,m): n=3, m=3 A| b 1.000000 -2.000000 2.000000 | 1.000000 -1.000000 2.000000 -1.000000 | 0.000000 -2.000000 4.000000 1.000000 | 3.000000 Permutationsvektor perm 3 2 1 Zerlegung PA=L\U -2.000000 4.000000 1.000000 0.500000 0.000000 -1.500000 -0.500000 0.000000 2.500000 ———————————————————————————————————— Kontrollrechnungen (j/n)? C1 = LU mittels laKopiere -2.000000 4.000000 1.000000 0.500000 0.000000 -1.500000 -0.500000 0.000000 2.500000 C2 = LU mittels laKopiere1 -2.000000 4.000000 1.000000 0.500000 0.000000 -1.500000 -0.500000 0.000000 2.500000 C3 = A+C2 -1.000000 2.000000 3.000000 -0.500000 2.000000 -2.500000 -2.500000 4.000000 3.500000 Normen von b 1.000000 0.000000 3.000000 ||b|| 1= 4.000000, ||b|| u= 3.000000, ||b|| 2= 3.162278 Normen von A 1.000000 -2.000000 2.000000 -1.000000 2.000000 -1.000000 -2.000000 4.000000 1.000000 ||A|| u= 7.000000, ||A|| 1= 8.000000, ||A|| F= 6.000000 64 Beispiel 5 Ax = b, 2 2 1 3 A= 2 4 A singulär, rang(A) = 2 5 2 4 , keine Lösung x∗ . 0 , b= 7 1 Abbruch des Gauß-Algorithmus, der ohne Zeilenvertauschung abläuft, im dritten Schritt. Bis dahin wird folgender “Zwischenstand“ erreicht. 2 2 2 1 0 0 L = 21 1 0 , U = 0 2 −1 . 1 1 1 0 0 0 Die Zerlegung A = LU ist möglich, da erst im letzten Schritt die Komponente u33 = 0 wird. Eingabe- und Ergebnisfenster 2 C:\d\Neundorf\nwptexte\tech phy\05\dev cpp\projekt4\gauss4c.exe Resttableau-Gauss-Algorithmus mit Spaltenpivotisierung und Zeilenvertauschung Tausch mit betragsgroessten NNE in der Spalte Ax=b, A => L\U, P, PA=LU Ux=c Loesung mit Rueckwaertseinsetzen ———————————————————————————————————— Dateiname fuer Daten A,b zum LGS Ax=b : Matrix58 Kontrollausgabe A,b (j/n)? Dimension A(n,m): n=3, m=3 A| b 2.000000 2.000000 2.000000 | 5.000000 1.000000 3.000000 0.000000 | 4.000000 2.000000 4.000000 1.000000 | 7.000000 ———————————————————————————————————— Matrix singulaer, Abbruch im Schritt = 3 Ergebnisprotokoll (j/n)? ———————————————————————————————————— Achtung! Vorzeitiger Abbruch im Schritt t = 3 Dimension A(n,m): n=3, m=3 A| b 2.000000 1.000000 2.000000 2.000000 3.000000 4.000000 2.000000 | 5.000000 0.000000 | 4.000000 1.000000 | 7.000000 Permutationsvektor perm 1 2 3 Zerlegung PA=L\U 2.000000 2.000000 2.000000 0.500000 2.000000 -1.000000 1.000000 1.000000 0.000000 65 Wir zeigen einige Modifikationen der bisherigen C-Projekts gauss4c in Bezug auf den Verzicht des Befehls getch() zur Zeicheneingabe ohne Bildschirmecho (bei C automatisch durch Einbinden der Bibliotheken vorhanden, bei C++ im Header-File conio.h). Dies betrifft mehrere Funktionen des Projekts. Die Funktion laGlSysEinlesen_ im Quelltext laInOut_c soll dazu als Musterbeispiel ausgewählt werden. Bisherige Version: ... void laGlSysEinlesen_(laMatrix *A, laMatrix *b) { FILE *matIn; int i,j,n,m; char s[80]="Matrix.in"; // Initialisierung printf("Dateiname fuer Daten A,b zum LGS Ax=b : "); scanf("%s",&s); matIn = fopen(s,"r"); if(matIn == NULL) { printf("Fehler: Datei nicht gefunden\n"); getch(); exit(EXIT_FAILURE); } fscanf(matIn,"%u %u",&n,&m); *A = laMatrixNeu_(n,m); *b = laMatrixNeu_(n,1); for(i=0;i<n;i++) for(j=0;j<m;j++) fscanf(matIn,"%lf",&(*A)[i][j]); for(i=0;i<n;i++) fscanf(matIn,"%lf",&(*b)[i][0]); fclose(matIn); } Mit der scanf-Eingabeanweisung wird eine Zeichenkette (Stream, String) für den Namen der Datei eingelesen. Die Eingabe schließt mit der <Enter>-Taste ( ◭ yp -Taste) ab. Alle Zeichen stehen zunächst im Eingabepuffer (Tastaturpuffer), um dann in die Variable s übertragen zu werden. Ist der Name unzulässig, kommt die Fehlerausschrift auf dem Monitor. Diese bleibt bis zu einer Tastenbetätigung angezeigt. Nun sollen zwei Dinge berücksichtigt werden. 1. Damit man sicher ist, dass der Eingabepuffer nach der Namenseingabe auf jeden Fall wieder leer und damit auch das <Enter>-Zeichen nach der Zeichenketteneingabe gelöscht wird, ergänzt man die Anweisung (Methode) fflush(stdin) mit stdin aus Header-File stdio.h. 2. Den getch()-Befehl ersetzt man durch die scanf("%c",&ret)-Anweisung mit der charVariablen ret. Version (a): im Projekt gauss4ca ... void laGlSysEinlesen_(laMatrix *A, laMatrix *b) { FILE *matIn; int i,j,n,m; char s[80]="Matrix.in"; // Initialisierung char ret; printf("Dateiname fuer Daten A,b zum LGS Ax=b : "); scanf("%s",&s); fflush(stdin); 66 matIn = fopen(s,"r"); if(matIn == NULL) { printf("Fehler: Datei nicht gefunden\n"); scanf("%c",&ret); exit(EXIT_FAILURE); } ... } Eine weitere Veränderung bedeutet den Verzicht auf die Anweisung fflush(stdin) durch das Ersetzen mit der scanf()-Anweisung. Dabei werden Dateiname (Zeichenkette) und abschließende <Enter>-Taste durch die scanf("%s%c",&s,&ret)-Eingabeanweisung korrekt verarbeitet und aus dem Eingabepuffer herausgelesen. Version (b): im Projekt gauss4cb ... void laGlSysEinlesen_(laMatrix *A, laMatrix *b) { FILE *matIn; int i,j,n,m; char s[80]="Matrix.in"; // Initialisierung char ret; printf("Dateiname fuer Daten A,b zum LGS Ax=b : "); scanf("%s%c",&s,&ret); matIn = fopen(s,"r"); if(matIn == NULL) { printf("Fehler: Datei nicht gefunden\n"); scanf("%c",&ret); exit(EXIT_FAILURE); } fscanf(matIn,"%u %u",&n,&m); *A = laMatrixNeu_(n,m); *b = laMatrixNeu_(n,1); for(i=0;i<n;i++) for(j=0;j<m;j++) fscanf(matIn,"%lf",&(*A)[i][j]); for(i=0;i<n;i++) fscanf(matIn,"%lf",&(*b)[i][0]); fclose(matIn); } Ausgehend vom C-Projekt in der Version gauss4cb soll es nun als C++-Projekt dargestellt werden und funktionieren. Bis auf die Speicherplatzzuweisung für den Zeigertyp typedef double **laMatrix im Header-File la_.h klappt alles. Nur die Anweisungen zur Allokation von dynamischen Speicher if ((A = malloc(sizeof(double*)*n+2*sizeof(int)))==NULL) ... if ((*(A+i) = malloc(sizeof(double)*m))==NULL) ... in der Funktion laMatrix laMatrixNeu_(int n, int m) aus dem Quelltext la_.cpp führen zur Fehlermeldung 23 32 C:\d\Neundorf\...\projekt4b\la_.cpp C:\d\Neundorf\...\projekt4b\la_.cpp C:\d\Neundorf\...\projekt4b\la_.cpp [Warning] In function ‘double ** laMatrixNeu_(int, int)’: ANSI C++ forbids implicit conversion from ‘void *’ in assignment ANSI C++ forbids implicit conversion from ‘void *’ in assignment 67 C++ kann bei der Allokation von Speicher mit dem Befehl malloc arbeiten. Aber die allokierte Variable muss als Zeigertyp mit einem Datentyp der Objekte stehen. Da man hier gar nicht über den Zeiger auf das referenzierte Objekt zugreifen will, sondern nur Speicheradressen und damit Adressen von Objekten beliebigen Typs verwaltet, ist ein Zeiger auf void (generischer Zeiger) zu verwenden. Das ist durch den vorangestellten Typ (void*) einfach zu machen. Weitere Veränderungen sind nicht erforderlich. Um später auf die referenzierten Objekte zugreifen zu können, muss der Zeiger dann aber zuerst in einen Zeiger auf den entsprechenden Datentyp des Objekts umgewandelt werden (Dereferenzierung, explizite Konvertierung der Adresse von void* in datentyp*). Version (c1): im Projekt gauss4cpp1 laMatrix laMatrixNeu_(int n, int m) { unsigned i; char ret; laMatrix A; if((n<1)||(m<1)) { printf("Fehler: Dimension(en) unzulaessig\n"); scanf("%c",ret); exit(EXIT_FAILURE); } if(((void*)A = malloc(sizeof(double*)*n+2*sizeof(int)))==NULL) { fprintf(stderr,"Zu wenig Speicher\n"); scanf("%c",ret); exit(EXIT_FAILURE); } *((int*)A) = n; *(((int*)A)+1) = m; A = (double**)(((int*)A) +2); // auch // auch *((int*)A+1) = m; A = (double**)((int*)A +2); for(i=0;i<n;i++) if(((void*)(*(A+i)) = malloc(sizeof(double)*m))==NULL) { fprintf(stderr,"Zu wenig Speicher\n"); scanf("%c",ret); exit(EXIT_FAILURE); } return(A); } Die elegantere C++-Version zur dynamischen Speicherallokation arbeitet mit dem Anweisungspaar (Operatoren) new und delete zur Reservierung bzw. Freigabe von Speicher. Man hat jedoch eine besondere Situation bei der Adressierung sowie jeweils Felder von Objekten. Der Anfangszeiger zeigt zunächst auf eine Objekt (Bereich), in dem die beiden Matrixdimensionen als integer-Zahlen n und m sowie die n Zeiger auf die Matrixzeilen mit jeweils m double-Komponenten stehen. Nachdem die Dimensionen an den ersten beiden “Stellen“ eingetragen wurden, wird der Anfangszeiger um zwei Positionen weitergerückt. Damit steht er am Beginn des eigentlichen Adressbereiches der Matrix mit ihren double-Elementen. Nun kann jedem Zeilenzeiger sein Speicherbereich entsprechend der Matrixzeile “zugewiesen“ werden. Der Zugriff auf die Matrixdimensionen geht dabei nicht verloren, denn mit dem Anfangszeiger kann nach Umtypisierung auf int* auch wieder rückwärts gezählt werden. 68 Version (c2): im Projekt gauss4cpp2 laMatrix laMatrixNeu_(int n, int m) { unsigned i; char ret; laMatrix A; if((n<1)||(m<1)) { printf("Fehler: Dimension(en) unzulaessig\n"); scanf("%c",ret); exit(EXIT_FAILURE); } if(((char*)A = new char[sizeof(double*)*n+2*sizeof(int)])==NULL) { fprintf(stderr,"Zu wenig Speicher\n"); scanf("%c",ret); exit(EXIT_FAILURE); } *((int*)A) = n; *(((int*)A)+1) = m; A = (double**)(((int*)A) +2); // auch // auch *((int*)A+1) = m; A = (double**)((int*)A +2); for(i=0;i<n;i++) if((*(A+i) = new double[m])==NULL) { fprintf(stderr,"Zu wenig Speicher\n"); scanf("%c",ret); exit(EXIT_FAILURE); } return(A); } /*----------------------------------------------------------------------------*/ void laVernichte_(laMatrix A) { unsigned i,n; n = *(((int*)A)-2); for(i=0;i<n;i++) delete [](*(A+i)); delete [](char*)(((int*)A) -2); // auch delete [](char*)((int*)A -2); } Schema der dynamischen Speicherverwaltung zu gauss4cpp2 laMatrix ↓ A→ A→ ւ (char*)A = new char[sizeof(double*)*n+2*sizeof(int)] int n int m double∗ → 1. Matrixzeile mit m double-Elementen double∗ → 2. Matrixzeile mit m double-Elementen → n. Matrixzeile mit m double-Elementen ւ *(A+i) = new double[m] ...... double∗ ↑ A = (double**)((int*)A +2); 69 4.5 Aufgabe 5 Ein weiteres Projekt zur Behandlung des Algorithmus der Vektoriteration und inversen Vektoriteration soll folgen. Dabei geht es um die iterative Bestimmung des einzigen betragsgrößten bzw. betragskleinsten Eigenwerts einer Matrix sowie des zugehörigen Eigenvektors (partielles Eigenwertproblem). Der zentrale Datentyp ist wieder ein Zeiger auf ein zweidimensionales Feld (Matrix) mit Komponenten vom Typ double. Vektoren werden in dieser Konvention als Feld mit einer Spalte interpretiert. Der Datentyp laMatrix zeigt auf eine Struktur, die als Informationen die Zeilen- und Spaltenmdimension sowie die Matrixelemente zeilenweise enthält. Die Nummerierung der Zeilen und Spalten beginnt in C/C++ jeweils bei Null. Die Vektoriteration braucht in jedem Iterationsschritt die Matrix-Vektor-Multiplikation und wegen der Skalierung die Berechnung der Vektornorm. Die inverse Vektoriteration hat anstelle der Matrix-Vektor-Multiplikation die Lösung eines LGS. Dabei wird jedoch der bisher verwendete Resttableau-Gauß-Algorithmus mit Spaltenpivotisierung und Zeilenvertauschung gauss4c(int n,int *t,laMatrix *A,laMatrix b,laMatrix *x,laMatrix *perm,int *sing) aus Abschnitt 4.4 zerlegt in zwei Prozeduren zur LU -Faktorisierung der Matrix A mit Pivostrategie (P A = LU, P Permutationsmatrix) sowie zur Lösung von Ax = b mittels gestaffelter LGS gemäß Ax = b, P Ax = LU x = L(U x) = P b, Lz = P b, U x = z. Die Matrix A ist Eingabematrix und wird überschrieben durch die LU -Zerlegung von P A, welche vor der Iteration einmal mit dem Aufwand O(n3 ) gemacht wird und dann zurückgegeben wird. Deshalb wird in der inversen Vektoriteration eine Kopie von A angelegt. Der Permutationsvektor von p=perm für die Zeilenvertauschungen ist eine einspaltige reelle Ergebnismatrix. Die Komponenten von p sind wertemäßig natürliche Zahlen, die zur Vertauschung im Vektor der rechten Seite gebraucht werden. Falls die Faktorisierung nicht durchführbar ist, so weist der Ergebnisparameter sing6= 0 darauf hin, wobei mit der Größe t noch die Stufe (Schritt im Faktorisierungs-Algorithmus) des Abbruchs angezeigt werden kann. In jedem Iterationsschritt der inversen Vektoriteration wird dann mit der Faktorisierung für die jeweils neue rechte Seite eine Vorwärts- und Rückwärtsrechnung mit dem Aufwand O(n2 ) vorgenommen. Damit ist diese Vorgehensweise natürlich effizienter als die Lösung eines LGS mittels Gauß-Algorithmus in jedem Schritt. Die inversen Vektoriteration ist nicht durchführbar, wenn A singulär ist. Dann wäre der betragsgrößte Eigenwert von A−1 “unendlich“. Für die Iteration braucht man einen nicht verschwindenden Startvektor, der wahlweise eingelesen bzw. als Einsvektor oder erster Einheitsvektor definiert wird. In allen Fällen wird die Iteration gesteuert durch eine maximale Iterationsanzahl maxiter und die Toleranz ε=eps für den Test der Übereinstimmung aufeinander folgender Iterierter (Beträge des Eigenwerts). Das Vorzeichen des gesuchten Eigenwerts wird anschließend noch aus dem Vorzeichenvergleich einer geeigneten Komponente der beiden letzten Iterationsvektoren bestimmt. Der Iterationsvektor wird skaliert und ist selber Näherung für den Eigenvektor. 70 Zunächst die beiden Funktionen zur LU -Faktorisierung der Matrix A mit Pivostrategie sowie zur Lösung von Ax = b mittels gestaffelter LGS. void gauss_LU(int n, int *t, laMatrix *A, laMatrix *perm, int *sing) { double max,s,eps,h; int i,j,k,index; char ret; /* Initialisierung */ *sing = 0; *t = 0; for(k=0;k<n;k++) (*perm)[k][0] = k; eps = 1e-15; /* Permutationsvektor */ /* Toleranz fuer Test auf Singularitaet */ /* Hinrechnung */ for(k=0;k<n;k++) { /* Pivotsuche */ max = 0; for(i=k;i<n;i++) { s = fabs((*A)[i][k]); if(s>max) { max = s; index = i; } } /* Test auf Singularitaet */ if(max<eps) { *sing = 1; *t = k+1; printf("\nMatrix singulaer, Abbruch im Schritt = %d\n",k+1); scanf("%c",&ret); exit(EXIT_FAILURE); } /* Zeilentausch */ if(index>k) { h = (*perm)[k][0]; /* laVertauscheZe_(*perm,k,index); */ (*perm)[k][0] = (*perm)[index][0]; (*perm)[index][0] = h; laVertauscheZe_(*A,k,index); } /* Bestimmung der Spaltenelemente und Zeilenelemente */ max = (*A)[k][k]; for(i=k+1;i<n;i++) { s = (*A)[i][k]/max; (*A)[i][k] = s; for(j=k+1;j<n;j++) (*A)[i][j] = (*A)[i][j]-s*(*A)[k][j]; } } } /*--------------------------------------------------------------------------------*/ void gestaffelte_LGS(int n, laMatrix LU, laMatrix b, laMatrix *x, laMatrix perm) { double h; int i,j; laMatrix bp,z; /* Permutation der rechten Seite */ bp = laMatrixNeu_(n,1); for (i = 0; i < n; i++) bp[i][0] = b[(int)perm[i][0]][0]; // bp[i][0] = b[(int)floor(perm[i][0]+1e-15)][0]; /* Hinrechnung Lz=bp */ z = laMatrixNeu_(n,1); z[0][0] = bp[0][0]; // mit einfacher Umtypisierung // sichere Variante 71 for(i=1;i<n;i++) { h = -bp[i][0]; for(j=0;j<i;j++) h += LU[i][j]*z[j][0]; z[j][0] = -h; } /* Rueckrechnung Ux=z */ for(i=n-1;i>=0;i--) { h = -z[i][0]; for(j=n-1;j>i;j--) h += LU[i][j]*(*x)[j][0]; (*x)[i][0] = -h/LU[i][i]; } laVernichte_(bp); laVernichte_(z); } Die Vektoriteration und inverse Vektoriteration notieren wir ebenfalls als Funktionen. Dabei sollen die Bibliotheksfunktionen wie – Initialisieren und Löschen von Feldern (Matrizen und Vektoren), – Kopieren von Feldern, – Normberechnung eines Vektors, – Multiplikation eines Vektors mit einer skalaren Größe, – Matrix-Vektor-Multiplikation, – Skalarprodukt zweier Vektoren, die in la_.h aufgelistet sind, so weit wie möglich Anwendung finden. void vektorit(int n, laMatrix A, int maxiter, double eps, laMatrix *x, double *la, int *iter) { double la_a,la_n,no; int i,j; laMatrix y,yn,yh; /* Initialisierung */ *iter = 0; y = laMatrixNeu_(n,1); yn = laMatrixNeu_(n,1); laKopiere_(*x,yn); no = laNorm_(’2’,yn); // sqrt(laSkalProd_(yn,yn)); la_n = 0; i = 0; do { la_a = la_n; laSkMult_(1.0/no,yn); laKopiere_(yn,y); laMult_(A,y,&yh); laKopiere_(yh,yn); i = i+1; no = laNorm_(’2’,yn); la_n = no; } while((fabs(la_a-la_n)>eps)&&(i<maxiter)); for(j=0;j<n;j++) if (y[j][0]*yn[j][0]!=0) break; if(y[j][0]*yn[j][0]<0) la_n = -la_n; laSkMult_(1.0/no,yn); /* Ergebnisse*/ laKopiere_(yn,*x); *iter = i; *la = la_n; laVernichte_(y); laVernichte_(yn); } /*--------------------------------------------------------------------------------*/ 72 void inv_vektorit(int n, laMatrix A, int maxiter, double eps, laMatrix *x, double *la, int *iter) { double la_a,la_n,no; int i,j,t,sing; char ret; laMatrix y,yn,LU,perm; /* Initialisierung */ *iter = 0; y = laMatrixNeu_(n,1); yn = laMatrixNeu_(n,1); LU = laMatrixNeu_(n,n); perm = laMatrixNeu_(n,1); laKopiere_(A,LU); gauss_LU(n,&t,&LU,&perm,&sing); if(sing!=0) { printf("\nMatrix singulaer -> Abbruch"); scanf("%c",&ret); exit(EXIT_FAILURE); } laKopiere_(*x,yn); no = laNorm_(’2’,yn); // sqrt(laSkalProd_(yn,yn)); la_n = 0; i = 0; do { la_a = la_n; laSkMult_(1.0/no,yn); laKopiere_(yn,y); gestaffelte_LGS(n,LU,y,&yn,perm); i = i+1; no = laNorm_(’2’,yn); la_n = no; } while((fabs(la_a-la_n)>eps)&&(i<maxiter)); for(j=0;j<n;j++) if(y[j][0]*yn[j][0]!=0) break; if(y[j][0]*yn[j][0]<0) la_n = -la_n; laSkMult_(1.0/no,yn); /* Ergebnisse*/ laKopiere_(yn,*x); *iter = i; *la = 1.0/la_n; laVernichte_(y); laVernichte_(yn); laVernichte_(LU); laVernichte_(perm); } Die Funktionen für die Ein- und Ausgabe sind problemangepasst und im Modul laInOut_.c zusammengefasst. Sie werden alle genutzt und noch durch zwei neue zur Matrixeingabe und -ausgabe ergänzt. Dort werden auch die Funktionen laMatrixNeu_, laDim_ aufgerufen. // laInOut_.c /* Eine Bibliothek fuer Lineare Algebra, Ein/Ausgaben */ /* Definitionen von laGlSysEinlesen_, laGlSysAusgeben_, laMatAusgeben_, laVektAusgeben_, laVektAusgebenI_ */ #include <stdlib.h> #include <stdio.h> //#include <conio.h> #include "la_.h" ... /*----------------------------------------------------------------------------*/ void laMatEinlesen_(laMatrix *A) { 73 FILE *matIn; int i,j,n,m; char s[80]="Matrix.in"; // Initialisierung printf("Dateiname fuer Daten zur Matrix A : "); scanf("%s",&s); // auch scanf("%s",s); matIn = fopen(s,"r"); // matIn = fopen("Matrix.in","r"); if(matIn==NULL) { printf("Fehler: Datei nicht gefunden\n"); getch(); exit(EXIT_FAILURE); } fscanf(matIn,"%u %u",&n,&m); *A = laMatrixNeu_(n,m); for(i=0;i<n;i++) for(j=0;j<m;j++) fscanf(matIn,"%lf",&(*A)[i][j]); fclose(matIn); } /*----------------------------------------------------------------------------*/ void laMatAusgeben_(laMatrix A) { int i,j,n,m; laDim_(A,&n,&m); for(i=0;i<n;i++) { for(j=0;j<m;j++) printf(" %10.6lf ",A[i][j]); printf("\n"); } } Die Definitionen befinden sich in verschiedenen Quelltexten sowie im Modul la_.c. Die Beispiele werden als ASCII-Dateien in der Datenreihenfolge n, m, A(1..n, 1..m), b(1..n), ... bereitgestellt. Der Dateiname für die Daten A, b zum LGS dient auch für die Bereitstellung der Matrix A selber. Als Testvarianten nehmen wir reguläre und singuläre Matrizen (reg.: matrix23, matrix3, matrix31, sing.: matrix58). matrix23 -------3 3 1 -2 2 -1 1 -1 -2 -2 1 1 -1 -3 Loesung: 1,1,1, matrix3 ------------------2 2 1.441969 1.040807 1.040807 0.751250 0.401162 0.289557 Loesung: 1,-1 matrix31 ---------------4 4 1 5 3 9 5 26 16 47 3 16 14 35 9 47 35 95 -12 -62 -38 -114 Loesung: 2,-1,3,-2 matrix58 -------3 3 2 2 2 1 3 0 2 4 1 5 4 7 Loesung: keine EW: 1 1 1 EW: 2.193218999999544 4.5595081932092e-13 EW: 0.031058762099 0.285539766776 3.409857734575 132.273543736549 EW: 0 1 5 74 Nun fehlt nur noch der Quelltext (Rahmenprogramm) meinprog_.c mit der main-Funktion. In ihm befinden sich die notwendigen include-Anweisungen, die genannten vier Funktionen zum partiellen Eigenwertproblem, Eingaben, Berechnungen und Ausgaben. // meinprog_.c #include<stdlib.h> #include<stdio.h> #include<math.h> #include "la_.h" void ... void ... void ... void ... gauss_LU(int n, int *t, laMatrix *A, laMatrix *perm, int *sing) gestaffelte_LGS(int n, laMatrix LU, laMatrix b, laMatrix *x, laMatrix perm) vektorit(int n, laMatrix A, int maxiter, double eps, laMatrix *x, double *la, int *iter) inv_vektorit(int n, laMatrix A, int maxiter, double eps, laMatrix *x, double *la, int *iter) int main() { int n,m,i,iter,maxiter; double eps,la; laMatrix A,x,x0; char ein,ret,kont; printf("Vektoriteration und inverse Vektoriteration fuer Eigenwerte einer Matrix\n"); printf("VI: y(m+1)=Ay(m), |la(A)|_max ~ ||y(m+1)||/||y(m)|| \n"); printf("inv. VI: Ay(m+1)=y(m), |la(A)|_min = 1/|la(A^-1)|_max \n"); printf("Loesung des LGS mittels LU-Faktorisierung und gestaffelter Systeme\n"); printf("-----------------------------------------------------------------------------\n\n"); laMatEinlesen_(&A); fflush(stdin); printf("Kontrollausgabe A (j/n)? "); scanf("%c",&kont,&ret); if(kont==’j’) { laDim_(A,&n,&m); printf("\nDimension A(n,m): n=%i, m=%i\n",n,m); printf("\nA\n"); laMatAusgeben_(A); printf("------------------------------------------------------------------------------\n\n"); scanf("%c",&ret); } laDim_(A,&n,&m); if(n!=m) { printf("\nDimension n<>m, keine Berechnung"); scanf("%c",&ret); return (-1); } x = laMatrixNeu_(n,1); x0 = laMatrixNeu_(n,1); fflush(stdin); printf("Eingabe/Belegung des Startvektors\n"); printf("Definition/Einsvektor/1.Einheitsvektor (d/e/i): "); scanf("%c",&ein,&ret); switch (ein) { case ’d’: for(i=0; i < n; i++) { printf("x0[%i] = ",i); scanf("%lf",&x0[i][0]); } break; case ’e’: for(i=0; i < n; i++) x0[i][0] = 1.0; break; default: x0 = laEins_(n,1); break; } printf("Eingabe Toleranz eps>0: "); scanf("%lf",&eps); printf("Eingabe maximale Iterationsanzahl maxiter>0: "); scanf("%i",&maxiter); 75 /* Berechnung */ laKopiere_(x0,x); vektorit(n,A,maxiter,eps,&x,&la,&iter); printf("\nVektoriteration\n"); printf("Betragsgroesster Eigenwert la = %19.16e\n",la); printf("Zugehoeriger skalierter Eigenvektor x\n"); laVektAusgeben_(x); printf("Iterationsanzahl iter = %i\n",iter); printf("---------------------------------------------------------------------------------------\n"); laKopiere_(x0,x); inv_vektorit(n,A,maxiter,eps,&x,&la,&iter); printf("\nInverse Vektoriteration\n"); printf("Betragskleinster Eigenwert la = %19.16e\n",la); printf("Zugehoeriger skalierter Eigenvektor x\n"); laVektAusgeben_(x); printf("Iterationsanzahl iter = %i\n",iter); printf("---------------------------------------------------------------------------------------\n"); fflush(stdin); scanf("%c",&ret); // Speicherplatz freigeben laVernichte_(A); laVernichte_(x); } Zusammenstellung des C-Projekts vektorit im Arbeitsverzeichnis C:\d\Neundorf\nwptexte\tech phy\05\dev cpp\projekt5. Im Projektfenster steht der Baum des Projekts. Die wirklich verwendeten Module sind fett hervorgehoben. S 2- –ℵ≡ vektorit ||− la .c | laAdd_.c |− | |− laEins .c | |− laElemMult_.c | |− laInOut .c | laKopiere .c |− | |− laKopiere1_.c | laKreuzProd_.c |− | laMult_.c |− | |− laNorm .c | laNull .c |− | |− laSkalProd .c | |− laSkMult .c | laSub_.c |− | |− laVertauscheSp_.c | laVertauscheZe .c |− | meinprog .c − Wie in den Projekten vorher kompiliert man das Projekt vektorit und führt es aus. Es folgen Informationen zum Übersetzen und im Kompilier Log. Im Verzeichnis stehen dann die Projektdatei vektorit.dev, die ausführbare Datei vektorit.exe sowie alle Objektdateien *.o der Projektquellen. Dazu kommt zum Projekt noch das sogenannte Makefile Makefile.win. 76 Beispiel 1 Ax = b, A regulär 1 −2 2 1 −1 , EW: 1, 1, 1 A = −1 −2 −2 1 Der einzige Eigenwert λ ist betragsgrößter und betragskleinster sowie mehrfach. Zugehöriger einziger Eigenvektor ist x = (−1, 1, 1)T , denn der Rang der Matrix A − λI ist 2. Die Vektoriteration konvergiert, aber wegen der Vielfachheit sehr langsam. Eingabe- und Ergebnisfenster 2 C:\d\Neundorf\nwptexte\tech phy\05\dev cpp\projekt5\vektorit.exe Vektoriteration und inverse Vektoriteration fuer Eigenwerte einer Matrix VI: y(m+1)=Ay(m), | la(A)| max ∼ ky(m+1)k/ky(m)k Inv. VI: Ay(m+1)=y(m), | la(A)| min = 1/|la(A∧ − 1 | max Loesung des LGS mittels LU-Faktorisierung und gestaffelter Systeme ——————————————————————————————————– Dateiname fuer Daten zur Matrix A : matrix23 Kontrollausgabe A (j/n)? j Dimension A(n,m): n=3, m=3 A 1.000000 -2.000000 2.000000 -1.000000 1.000000 -1.000000 -2.000000 -2.000000 1.000000 ——————————————————————————————————– Eingabe/Belegung des Startvektors Definition/Einsvektor/1.Einheitsvektor (d/e/i): d x0[0] = 1 x0[1] = 2 x0[2] = 3 Eingabe Toleranz eps>0: 1e-12 Eingabe maximale Iterationsanzahl maxiter>0: 300 Vektoriteration Betragsgroesster Eigenwert la = 1.0067340826915268e+000 Zugehoeriger skalierter Eigenvektor x -0.579284 0.577350 0.575410 Iterationsanzahl iter = 300 ——————————————————————————————————– Inverse Vektoriteration Betragskleinster Eigenwert la = 9.9337741046664496e-001 Zugehoeriger skalierter Eigenvektor x -0.575435 0.577350 0.579259 Iterationsanzahl iter = 300 ——————————————————————————————————– Ähnlich ist es mit den Startvektoren als Einsvektor oder erster Einheitsvektor. Beginnt man mit dem Startvektor (−1, 1, 1)T (Eigenvektor), dann konvergieren beide Varianten nach 2 Schritten und es sind la=1.000000000000 und der skalierte Eigenvektor (−0.577350, 0.577350, 0.577350)T . 77 Beispiel 2 Ax = b, A regulär und symmetrisch, det(A) = 10−12 1.441969 1.040807 A= , EW: 2.193218999999544, 4.5595081932092e − 13 1.040807 0.751250 Eingabe- und Ergebnisfenster 2 C:\d\Neundorf\nwptexte\tech phy\05\dev cpp\projekt5\vektorit.exe Vektoriteration und inverse Vektoriteration fuer Eigenwerte einer Matrix VI: y(m+1)=Ay(m), | la(A)| max ∼ ky(m+1)k/ky(m)k Inv. VI: Ay(m+1)=y(m), | la(A)| min = 1/|la(A∧ − 1 | max Loesung des LGS mittels LU-Faktorisierung und gestaffelter Systeme ——————————————————————————————————– Dateiname fuer Daten zur Matrix A : matrix3 Kontrollausgabe A (j/n)? n Eingabe/Belegung des Startvektors Definition/Einsvektor/1.Einheitsvektor (d/e/i): d x0[0] = 1 x0[1] = 2 Eingabe Toleranz eps>0: 1e-15 Eingabe maximale Iterationsanzahl maxiter>0: 10 Vektoriteration Betragsgroesster Eigenwert la = 2.1932189999995439e+000 Zugehoeriger skalierter Eigenvektor x 0.810843 0.585263 Iterationsanzahl iter = 3 ——————————————————————————————————– Inverse Vektoriteration Betragskleinster Eigenwert la = 4.5591736506873682e-013 Zugehoeriger skalierter Eigenvektor x -0.585263 0.810843 Iterationsanzahl iter = 3 ——————————————————————————————————– Ähnlich ist es mit den Startvektoren als Einsvektor oder erster Einheitsvektor. Auch bei noch kleineren Toleranzen erhält man die gleiche Anzahl von Iterationen. Mit der double-Genauigkeit können bei der Berechnung des betragskleinsten Eigenwerts nur ungefähr die ersten 4 Dezimalstellen garantiert werden. Noch eine Rechnung mit dem Startvektor (100, −100)T und sonst ohne Veränderungen. ... Vektoriteration Betragsgroesster Eigenwert la = 2.1932189999995444e+000 Zugehoeriger skalierter Eigenvektor x 0.810843 0.585263 Iterationsanzahl iter = 3 ——————————————————————————————————– Inverse Vektoriteration Betragskleinster Eigenwert la = 4.5591736506873682e-013 Zugehoeriger skalierter Eigenvektor x 0.585263 -0.810843 Iterationsanzahl iter = 4 ——————————————————————————————————– 78 Beispiel 3 Ax = b, A 1 5 3 5 26 16 A= 3 16 14 9 47 35 symmetrisch positiv definit, damit regulär 9 47 , 35 95 EW: 0.031058762099, 0.285539766776, 3.409857734575, 132.273543736549 Die betragsgrößten und betragskleinsten Eigenwerte sind gut separiert von den anderen, so dass die Iterationen schnell konvergieren. Eingabe- und Ergebnisfenster 2 C:\d\Neundorf\nwptexte\tech phy\05\dev cpp\projekt5\vektorit.exe Vektoriteration und inverse Vektoriteration fuer Eigenwerte einer Matrix VI: y(m+1)=Ay(m), | la(A)| max ∼ ky(m+1)k/ky(m)k Inv. VI: Ay(m+1)=y(m), | la(A)| min = 1/|la(A∧ − 1 | max Loesung des LGS mittels LU-Faktorisierung und gestaffelter Systeme ——————————————————————————————————– Dateiname fuer Daten zur Matrix A : matrix31 Kontrollausgabe A (j/n)? j Dimension A(n,m): n=4, m=4 A 1.000000 5.000000 3.000000 9.000000 5.000000 26.000000 16.000000 47.000000 3.000000 16.000000 14.000000 35.000000 9.000000 47.000000 35.000000 95.000000 ——————————————————————————————————– Eingabe/Belegung des Startvektors Definition/Einsvektor/1.Einheitsvektor (d/e/i): d x0[0] = 1 x0[1] = 2 x0[2] = 3 x0[3] = 4 Eingabe Toleranz eps>0: 1e-12 Eingabe maximale Iterationsanzahl maxiter>0: 20 Vektoriteration Betragsgroesster Eigenwert la = 1.3227354373654941e+002 Zugehoeriger skalierter Eigenvektor x 0.081311 0.424912 0.310068 0.846579 Iterationsanzahl iter = 6 ——————————————————————————————————– Inverse Vektoriteration Betragskleinster Eigenwert la = 3.1058762099130690e-002 Zugehoeriger skalierter Eigenvektor x 0.980914 -0.140818 0.116582 -0.066234 Iterationsanzahl iter = 10 ——————————————————————————————————– 79 Weitere Berechnungen mit anderen Startvektoren 2 C:\d\Neundorf\nwptexte\tech phy\05\dev cpp\projekt5\vektorit.exe Vektoriteration und inverse Vektoriteration fuer Eigenwerte einer Matrix VI: y(m+1)=Ay(m), | la(A)| max ∼ ky(m+1)k/ky(m)k Inv. VI: Ay(m+1)=y(m), | la(A)| min = 1/|la(A∧ − 1 | max Loesung des LGS mittels LU-Faktorisierung und gestaffelter Systeme ——————————————————————————————————– Dateiname fuer Daten zur Matrix A : matrix31 Kontrollausgabe A (j/n)? n Eingabe/Belegung des Startvektors Definition/Einsvektor/1.Einheitsvektor (d/e/i): e Eingabe Toleranz eps>0: 1e-12 Eingabe maximale Iterationsanzahl maxiter>0: 20 Vektoriteration Betragsgroesster Eigenwert la = 1.3227354373654941e+002 Zugehoeriger skalierter Eigenvektor x 0.081311 0.424912 0.310068 0.846579 Iterationsanzahl iter = 6 ——————————————————————————————————– Inverse Vektoriteration Betragskleinster Eigenwert la = 3.1058762099130683e-002 Zugehoeriger skalierter Eigenvektor x 0.980914 -0.140818 0.116582 -0.066234 Iterationsanzahl iter = 9 ——————————————————————————————————– ... Dateiname fuer Daten zur Matrix A : matrix31 Kontrollausgabe A (j/n)? n Eingabe/Belegung des Startvektors Definition/Einsvektor/1.Einheitsvektor (d/e/i): i Eingabe Toleranz eps>0: 1e-12 Eingabe maximale Iterationsanzahl maxiter>0: 20 Vektoriteration Betragsgroesster Eigenwert la = 1.3227354373654939e+002 Zugehoeriger skalierter Eigenvektor x 0.081311 0.424912 0.310068 0.846579 Iterationsanzahl iter = 7 ——————————————————————————————————– Inverse Vektoriteration Betragskleinster Eigenwert la = 3.1058762099130676e-002 Zugehoeriger skalierter Eigenvektor x 0.980914 -0.140818 0.116582 -0.066234 Iterationsanzahl iter = 8 ——————————————————————————————————– 80 Beispiel 4 Ax = b, 2 2 1 3 A= 2 4 A singulär, rang(A) = 2 2 0 , EW: 0, 1, 5 1 Die inverse Vektoriteration für den betragskleinsten Eigenwert ist nicht durchführbar. Schon bei der LU -Faktorisierung der Matrix A wird ihre Singularität festgestellt und signalisiert. Eingabe- und Ergebnisfenster 2 C:\d\Neundorf\nwptexte\tech phy\05\dev cpp\projekt5\vektorit.exe Vektoriteration und inverse Vektoriteration fuer Eigenwerte einer Matrix VI: y(m+1)=Ay(m), | la(A)| max ∼ ky(m+1)k/ky(m)k Inv. VI: Ay(m+1)=y(m), | la(A)| min = 1/|la(A∧ − 1 | max Loesung des LGS mittels LU-Faktorisierung und gestaffelter Systeme ——————————————————————————————————– Dateiname fuer Daten zur Matrix A : matrix58 Kontrollausgabe A (j/n)? j Dimension A(n,m): n=3, m=3 A 2.000000 2.000000 2.000000 1.000000 3.000000 0.000000 2.000000 4.000000 1.000000 ——————————————————————————————————– Eingabe/Belegung des Startvektors Definition/Einsvektor/1.Einheitsvektor (d/e/i): d x0[0] = 1 x0[1] = 2 x0[2] = 3 Eingabe Toleranz eps>0: 1e-12 Eingabe maximale Iterationsanzahl maxiter>0: 20 Vektoriteration Betragsgroesster Eigenwert la = 5.0000000000001322e+000 Zugehoeriger skalierter Eigenvektor x 0.666667 0.333333 0.666667 Iterationsanzahl iter = 19 ——————————————————————————————————– Matrix singulaer, Abbruch im Schritt = 3 Weitere Berechnungen Startvektor Startvektor Startvektor Startvektor Startvektor Startvektor (1, 1, 1)T , (1, 1, 1)T , (1, 0, 0)T , (1, 0, 0)T , (1,−2, 3)T , (1,−2, 3)T , ε = 10−12 : ε = 10−15 : ε = 10−12 : ε = 10−15 : ε = 10−12 : ε = 10−15 : la la la la la la = = = = = = 5.0000000000002496e+000, 5.0000000000000000e+000, 5.0000000000000009e+000, 5.0000000000000000e+000, 5.0000000000001625e+000, 5.0000000000000009e+000, 81 iter iter iter iter iter iter = = = = = = 19 27 3 5 21 25 5 Felder in C++ und C In den Programmen matsum.cpp, matsum.c (Abschnitt 1.4) sowie den gerade genannten Projekten gauss4c, gauss4ca, gauss4cb, gauss4cpp1, gauss4cpp2, vektorit zum Gauß-Algorithmus / Vektoriteration und speziell in den zahlreichen Definitionen/Modulen wie z. B. laMatrixNeu_, laVernichte_, laGlSysEinlesen_, ist der Umgang mit Matrizen und Vektoren und darauf aufbauenden Objekten schon implementiert worden. Es soll jedoch noch einmal in übersichtlicher Form die Deklaration, Definition und Verarbeitung von Feldern unter verschiedenen Aspekten dargestellt werden. Das betrifft nicht nur die Unterschiede zwischen den Sprachdialekten C++ und C, sondern auch die Verwendung von statischen und dynamischen Strukturen. Zunächst noch einmal die Zuordnung von einigen Objekten und Standardbezeichnern zu Header-Files. C++ #include <iostream.h> #include <conio.h> #include <stdlib.h> #include <stdlib.h> #include <stdio.h> #include <math.h> // // // // fuer Objekte wie cin, cout,... getch, printf, scanf, stdin,... EXIT_FAILURE,... // koennen sonst entfallen C //#include <iostream.h> // muss entfallen #include <math.h> // falls mathematische Standardfunktionen auftreten //getch, printf, scanf, ... werden automatisch erkannt #include <stdlib.h> #include <stdio.h> // fuer Objekte wie // EXIT_FAILURE,... // stdin, stderr, stdout,... #include <conio.h> // stdin,... 5.1 enthaelt Header-File stdio.h Statische Felder Statische bzw. nichtdynamische Felder (Array) sind hintereinander abgelegte Objekte gleichen Datentyps. Hier muss die Anzahl der Dimensionen und Elemente in einem Feld bei seiner Deklaration (also zur Programmierzeit) angegeben werden. Die Anwendung des Indizierungsoperators auf den Feldnamen ist nur deshalb möglich, weil der Compiler den Feldnamen in Anweisungen als Zeiger auf das erste Element de Feldes ansieht. Die Indizierung wird intern in die Zeigerberechnung umgewandelt. Eindimensionales Feld: feld[i] ⇔ *(feld+i), i=0,1,2,... Zeigerarithmetik bedeutet, dass der Wert eines Zeigers bei der Addition oder Subtraktion nicht in Einheiten von Bytes, sondern in Einheiten von der Größe der Objekte, auf die der Zeiger zeigt, erhöht oder vermindert wird. Die gezeigten Programme arbeiten mit einem zweidimensionalen Feld. Es sind verschiedene Möglichkeiten der Deklaration der Anzahl der Elemente in den beiden Dimensionen gezeigt. Von diesem “maximalen“ Feld wird in Abhängigkeit von der eingegebenen Zeilen- und Spaltenzahl nur ein Teil genutzt. Würden diese beiden Angaben (oder auch nur eine davon) die vereinbarte obere Grenze übersteigen, kommt es i. Allg. irgendwann während der Programmabarbeitung zu einer Fehlermeldung, in C++ eher als in C. a1stat.exe hat Fehler verursacht und wird geschlossen. Starten Sie das Programm neu. Ein Fehlerprotokoll wird erstellt. 82 C++ // a1stat.cpp // Felder: statisch // Berechnen der Zeilensummennorm einer (n,m)-Matrix #include <iostream.h> #include <conio.h> //#define nmax 50 int main() { const int nmax=50; // bzw. int nmax=50; double A[nmax][nmax],sum,max; int n,m,i,j; cout<<"Zeilensummennorm einer max. (50,50)-Matrix\n\n"; do { cout<<"Eingabe Zeilenzahl n (0<n<=50): "; cin>>n; } while ((n<=0)||(n>50)); do { cout<<"Eingabe Spaltenzahl m (0<m<=50): "; cin>>m; } while ((m<=0)||(m>50)); for(i=0;i<n;i++) { for(j=0;j<m;j++) { cout<<"A["<<i<<","<<j<<"] = "; cin>>A[i][j]; } } max=0.0; for(i=0;i<n;i++) { sum=0.0; for(j=0;j<m;j++) sum=sum+fabs(A[i][j]); max=(sum>max?sum:max); } // sum+=fabs(A[i][j]); // if (sum>max) max=sum; cout<<"\nZeilensummennorm = "<<max; getch(); return 0; } C // a1stat.c // Felder: statisch // Berechnen der Zeilensummennorm einer (n,m)-Matrix //#include <iostream.h> #include <conio.h> #include <math.h> //#define nmax 50 int main() { const int nmax=50; // bzw. int nmax=50; double A[nmax][nmax],sum,max; int n,m,i,j; char ret; printf("Zeilensummennorm einer max. (50,50)-Matrix\n\n"); do { printf("Eingabe Zeilenzahl n (0<n<=50): "); 83 scanf("%i",&n); } while((n<=0)||(n>50)); do { printf("Eingabe Spaltenzahl m (0<m<=50): "); scanf("%i",&m); } while((m<=0)||(m>50)); for(i=0;i<n;i++) { for(j=0;j<m;j++) { printf("A[%i,%i] = ",i,j); scanf("%lf",&A[i][j]); } } max=0.0; for(i=0;i<n;i++) { sum=0.0; for(j=0;j<m;j++) sum=sum+fabs(A[i][j]); max=(sum>max?sum:max); } // sum+=fabs(A[i][j]); // if (sum>max) max=sum; printf("\nZeilensummennorm = %lf",max); // Tastaturpuffer leeren fflush(stdin); // stdin in stdio.h bzw. conio.h scanf("%c",&ret); // anstelle von getch() return 0; } Eingabe- und Ergebnisfenster zu a1stat.c 2 C:\d\Neundorf\nwptexte\tech phy\05\cpp gpp gcc\a1stat.exe Zeilensummennorm einer max. (50,50)-Matrix Eingabe Zeilenzahl n (0<n<=50): 2 Eingabe Spaltenzahl m (0<m<=50): 5 A[0,0] = 1 A[0,1] = 2 A[0,2] = 3 A[0,3] = -1 A[0,4] = -2 A[1,0] = 4 A[1,1] = 5 A[1,2] = 6 A[1,3] = 0 A[1,4] = -1 Zeilensummennorm = 16.000000 84 5.2 Dynamische Felder Dynamische Felder (Array) sind ebenfalls Objekte gleichen Datentyps. Hier muss die Anzahl der Dimensionen und Elemente in einem Feld erst zur Laufzeit des Programms feststehen und kann sich daher den aktuellen Gegegebenheiten anpassen. Die dynamische Reservierung von Feldern ist mit der Verwendung von Zeigern verbunden. Die Anwendung des Indizierungsoperators auf den Feldnamen ist deshalb möglich, weil der Compiler den Feldnamen in Anweisungen als Zeiger auf das erste Element de Feldes ansieht. Die Indizierung und Zeigerberechnung sind ineinander umwandelbar. Die dynamische Speicherallokation mittels den Zeiger reserviert den gewünschten Speicherplatz, der dann üblicherweise auch wieder freizugeben ist, bevor der Zeiger nicht mehr genutzt, ungültig oder umgelenkt wird (Vermeidung von so genannten Speicherlecks). Speicherallokation Speicherfreigabe ----------------------------------------------------C++ new delete C++/C malloc() free() In der Zeigerprogrammierung sind u. a. wichtig – – – – – – die Deklaration von Zeigern mit dem bestimmten Datentyp, generische Zeiger (void) ohne Typbezogenheit, explizite Typumwandlung bei Zeigern, Zeiger und Felder, Zeigerarithmetik und Zeiger auf Zeiger. Die Typumwandlung bei Zeigern ist in den Projekten gauss4c,..., gauss4cpp2 zum Gauß-Algorithmus und speziell in der Funktion laMatrixNeu_ schon mehrfach verwendet worden. Am Beispiel eines zweidimensionalen Felds (rechteckige Matrix) sollen einige Aspekte demonstriert werden. Halbdynamische Felder Zunächst soll speicherplatzsparend dadurch gearbeitet werden, dass bei fester maximaler Matrixzeile mit spmax Elementen (=Spalten) die Anzahl der Zeilen in Abhängigkeit von der aktuell eingegebenen Größe n festgelegt wird. Für die Matrixelemente braucht man dann n*spmax Speicherplatz von gegebenem Elementtyp (hier double). Dazu kommt noch Speicherplatz für etwas Adressverwaltung. C++ Das ist die kurze und elegante Version unter Verwendung eines Zeigervektors (Vektor von Zeigern). Damit liegen die Matrixzeilen im Speicher genau hintereinander und es ist A[i] = *(A+i). Die Matrixelemente können mittels A[i][j] oder *(*(A+i)+j) aufgerufen werden. // a1hdyn1.cpp // Felder: halbdynamisch, Zeigervektor, new-delete // Berechnen der Zeilensummennorm einer (n,m)-Matrix #include <iostream.h> #include <conio.h> 85 //#define spmax 50 int main() { const int spmax=50; typedef double vektor[spmax]; // maximale Spaltenzahl // Typ des Zeilenvektors int i,j,n,m; double max,sum; cout<<"Zeilensummennorm einer (n,m)-Matrix, m<=50\n\n"; do { cout<<"Eingabe Zeilenzahl n (0<n): "; cin>>n; } while(n<=0); do { cout<<"Eingabe Spaltenzahl m (0<m<=50): "; cin>>m; } while((m<=0)||(m>50)); // Allokation der Matrix auf dem Heap, damit vektor *A = new vektor[n]; // n beliebig A[i] = *(A+i) for(i=0;i<n;i++) for(j=0;j<m;j++) { cout<<"A["<<i<<","<<j<<"]= "; cin>>A[i][j]; } max=0.0; for(i=0;i<n;i++) { sum=0.0; for(j=0;j<m;j++) sum=sum+fabs(A[i][j]); // sum+=fabs(A[i][j]); max=(sum>max?sum:max); // if (sum>max) max=sum; } cout<<"\nZeilensummennorm = "<<max; delete []A; getch(); return 0; } C Verwendung des Zeigervektors. Ein Feld von Zeigern (Zeigerfeld) auf den Datentyp T wird als T *ptr[anzahl] deklariert. Dies ist nicht mit einem Zeiger auf ein Feld vom Typ T gemäß T (*ptr)[anzahl] zu verwechseln. Da die Deklaration des Zeigerfelds mit einer festen Feldgrenze erfolgen muss, gibt man dort eine maximale obere Schranke an. Die eingegebene Anzahl der Zeilen sollte dann nicht größer als diese Schranke sein. Die Dynamik der Allokation von Speicherplatz für die Matrixzeilen besteht dann darin, dass man nur Platz für die gewünschten Zeilen allokiert. Ist die eingegebene Zeilenzahl größer als die Schranke, wird es i. Allg. während der Programmabarbeitung zur Fehlermeldung und damit zum Abbruch kommen. Programmfehler a1stat.exe hat Fehler verursacht und wird geschlossen. Starten Sie das Programm neu. Ein Fehlerprotokoll wird erstellt. Damit ist die dynamische Speicherverwaltung bezüglich der Zeilen nicht vollständig machbar. 86 // a1hdyn1.c // Felder: halbdynamisch mit Kontrolle, Zeigervektor, malloc-free // Berechnen der Zeilensummennorm einer (n,m)-Matrix #include <conio.h> #include <stdlib.h> #include <math.h> //#define spmax 50 //#define zemax 50 int main() { const int spmax=50; const int zemax=50; typedef double vektor[spmax]; double *A[zemax]; // // // // maximale Spaltenzahl maximale Zeilenzahl Typ des Zeilenvektors Feld (Vektor) von Zeigern int i,j,n,m; double max,sum; char ret; printf("Zeilensummennorm einer (n,m)-Matrix, n,m<=50\n\n"); do { printf("Eingabe Zeilenzahl n (0<n<=50): "); scanf("%i",&n); } while((n<=0)||(n>50)); do { printf("Eingabe Spaltenzahl m (0<m<=50): "); scanf("%i",&m); } while(m<=0||m>50); // Allokation der Matrix auf dem Heap for(i=0;i<n;i++) //if((*(A+i) = (double*) malloc(sizeof(vektor)))==NULL) if((A[i] = (double*) malloc(sizeof(vektor)))==NULL) { printf("Zu wenig Speicher\n"); scanf("%c",&ret); exit(EXIT_FAILURE); } // analog for(i=0;i<n;i++) for(j=0;j<m;j++) { printf("A[%i,%i] = ",i,j); scanf("%lf",&A[i][j]); } max=0.0; for(i=0;i<n;i++) { sum=0.0; for(j=0;j<m;j++) sum=sum+fabs(A[i][j]); // sum+=fabs(A[i][j]); // sum=sum+fabs(*(*(A+i)+j)); // if (sum>max) max=sum; max=(sum>max?sum:max); } printf("\nZeilensummennorm = %lf",max); fflush(stdin); for(i=0;i<n;i++) free(*(A+i)); scanf("%c",&ret); return 0; } Nun soll ein Zeiger auf Zeiger genutzt werden. Dabei erkennt man, was notwendig wäre, um auch in der Spaltendimension dynamisch zu werden. Man braucht die Reservierung der Matrixzeilen nur mit der eingelesenen Länge m anstelle von spmax zu machen. 87 // a1hdyn2.c // Felder: halbdynamisch mit Kontrolle, Zeiger auf Zeiger, malloc-free // Berechnen der Zeilensummennorm einer (n,m)-Matrix #include <conio.h> #include <stdlib.h> #include <math.h> //#define spmax 50 int main() { const int spmax=50; typedef double vektor[spmax]; double **A; // maximale Spaltenzahl // Typ des Zeilenvektors int i,j,n,m; double max,sum; char ret; printf("Zeilensummennorm einer (n,m)-Matrix, m<=50\n\n"); do { printf("Eingabe Zeilenzahl n (0<n): "); scanf("%i",&n); } while (n<=0); do { printf("Eingabe Spaltenzahl m (0<m<=50): "); scanf("%i",&m); } while ((m<=0)||(m>50)); // Allokation der Matrix auf dem Heap if((A = (double**) malloc(n*sizeof(double*)))==NULL) { printf("Zu wenig Speicher\n"); scanf("%c",&ret); exit(EXIT_FAILURE); } for(i=0;i<n;i++) { // if((*(A+i) = (double*) malloc(spmax*sizeof(double)))==NULL) if((*(A+i) = (double*) malloc(sizeof(vektor)))==NULL) { printf("Zu wenig Speicher\n"); scanf("%c",&ret); exit(EXIT_FAILURE); } } for(i=0;i<n;i++) for(j=0;j<m;j++) { printf("A[%i,%i] = ",i,j); scanf("%lf",&A[i][j]); } max=0.0; for(i=0;i<n;i++) { sum=0.0; for(j=0;j<m;j++) sum=sum+fabs(A[i][j]); // sum+=fabs(A[i][j]); max=(sum>max?sum:max); // if (sum>max) max=sum; } printf("\nZeilensummennorm = %lf",max); fflush(stdin); for(i=0;i<n;i++) free(*(A+i)); free(A); scanf("%c",&ret); return 0; } 88 // analog Dynamische Felder Verwendung des Konzepts Zeiger auf Zeiger für beide Sprachdialekte. C++ 4 Programmversionen // a1dyn1.cpp // Felder: dynamisch, Zeiger auf Zeiger, new-delete // Berechnen der Zeilensummennorm einer (n,m)-Matrix #include <iostream.h> #include <conio.h> int main() { typedef double **matrix; matrix A; // double **A; // Typ der Matrix // einfache Schreibweise int i,j,n,m; double max,sum; cout<<"Zeilensummennorm einer (n,m)-Matrix\n\n"; do { cout<<"Eingabe Zeilenzahl n (0<n): "; cin>>n; } while(n<=0); do { cout<<"Eingabe Spaltenzahl m (0<m): "; cin>>m; } while(m<=0); // Allokation der Matrix auf dem Heap // mit Umtypisierung // einfache Variante (char*)A = new char[n*sizeof(double*)]; // A = new double*[n]; a1dyn2.cpp for(i=0;i<n;i++) *(A+i) = new double[m]; for(i=0;i<n;i++) for(j=0;j<m;j++) { cout<<"A["<<i<<","<<j<<"]= "; cin>>A[i][j]; } max=0.0; for(i=0;i<n;i++) { sum=0.0; for(j=0;j<m;j++) sum=sum+fabs(A[i][j]); // sum+=fabs(A[i][j]); max=(sum>max?sum:max); // if (sum>max) max=sum; } cout<<"\nZeilensummennorm = "<<max; for(i=0;i<n;i++) delete [](*(A+i)); delete [](char*)A; // einfache Variante // delete []A; a1dyn2.cpp getch(); return 0; } /*-------------------------------------------------------------------------------------*/ 89 // a1dyn3.cpp // Felder: dynamisch mit Kontrolle, Zeiger auf Zeiger, new-delete // Berechnen der Zeilensummennorm einer (n,m)-Matrix #include <iostream.h> #include <conio.h> #include <stdlib.h> int main() { typedef double **matrix; matrix A; // double **A; // Typ der Matrix // einfache Schreibweise int i,j,n,m; double max,sum; cout<<"Zeilensummennorm einer (n,m)-Matrix\n\n"; do { cout<<"Eingabe Zeilenzahl n (0<n): "; cin>>n; } while(n<=0); do { cout<<"Eingabe Spaltenzahl m (0<m): "; cin>>m; } while(m<=0); // Allokation der Matrix auf dem Heap // Fehlermeldung? // ev. "Nicht genuegend virtueller Speicher, // Auslagerungsdatei vergroessern,..." if ((A = new double*[n])==NULL) { printf("Zu wenig Speicher\n"); getch(); exit(EXIT_FAILURE); } for(i=0;i<n;i++) if((*(A+i) = new double[m])==NULL) { printf("Zu wenig Speicher\n"); getch(); exit(EXIT_FAILURE); } for(i=0;i<n;i++) for(j=0;j<m;j++) { cout<<"A["<<i<<","<<j<<"]= "; cin>>A[i][j]; } max=0.0; for(i=0;i<n;i++) { sum=0.0; for(j=0;j<m;j++) sum=sum+fabs(A[i][j]); // sum+=fabs(A[i][j]); max=(sum>max?sum:max); // if (sum>max) max=sum; } cout<<"\nZeilensummennorm = "<<max; for(i=0;i<n;i++) delete [](*(A+i)); delete [](char*)A; getch(); return 0; } /*-------------------------------------------------------------------------------------*/ 90 // a1dyn4.cpp // Felder: dynamisch mit Kontrolle, Zeiger auf Zeiger, malloc-free // Berechnen der Zeilensummennorm einer (n,m)-Matrix //#include <iostream.h> #include <conio.h> #include <stdlib.h> int main() { typedef double **matrix; matrix A; // double **A; // Typ der Matrix // einfache Schreibweise int i,j,n,m; double max,sum; char ret; printf("Zeilensummennorm einer (n,m)-Matrix\n\n"); do { printf("Eingabe Zeilenzahl n (0<n): "); scanf("%i",&n); } while(n<=0); do { printf("Eingabe Spaltenzahl m (0<m): "); scanf("%i",&m); } while(m<=0); // Allokation der Matrix auf dem Heap if(((void*)A = malloc(n*sizeof(double*)))==NULL) { printf("Zu wenig Speicher\n"); scanf("%c",&ret); exit(EXIT_FAILURE); } for(i=0;i<n;i++) if(((void*)(*(A+i)) = malloc(m*sizeof(double)))==NULL) { printf("Zu wenig Speicher\n"); scanf("%c",&ret); exit(EXIT_FAILURE); } for(i=0;i<n;i++) for(j=0;j<m;j++) { printf("A[%i,%i] = ",i,j); scanf("%lf",&A[i][j]); } max=0.0; for(i=0;i<n;i++) { sum=0.0; for(j=0;j<m;j++) sum=sum+fabs(A[i][j]); // sum+=fabs(A[i][j]); max=(sum>max?sum:max); // if (sum>max) max=sum; } printf("\nZeilensummennorm = %lf",max); fflush(stdin); for(i=0;i<n;i++) free(*(A+i)); free(A); scanf("%c",&ret); return 0; } Die Analogie zur Version a1dyn4.cpp ohne den generischen Zeiger void* gibt es auch in C. 91 C // a1dyn4.c // Felder: dynamisch mit Kontrolle, Zeiger auf Zeiger, malloc-free // Berechnen der Zeilensummennorm einer (n,m)-Matrix #include <conio.h> #include <math.h> #include <stdlib.h> int main() { typedef double **matrix; matrix A; // double **A; // Typ der Matrix // einfache Schreibweise int i,j,n,m; double max,sum; char ret; printf("Zeilensummennorm einer (n,m)-Matrix\n\n"); do { printf("Eingabe Zeilenzahl n (0<n): "); scanf("%i",&n); } while(n<=0); do { printf("Eingabe Spaltenzahl m (0<m): "); scanf("%i",&m); } while(m<=0); // Allokation der Matrix auf dem Heap // Fehlermeldung? // ev. "Nicht genuegend virtueller Speicher, // Auslagerungsdatei vergroessern,..." if((A = malloc(n*sizeof(double*)))==NULL) { printf("Zu wenig Speicher\n"); scanf("%c",&ret); exit(EXIT_FAILURE); } for(i=0;i<n;i++) if((*(A+i) = malloc(m*sizeof(double)))==NULL) { printf("Zu wenig Speicher\n"); scanf("%c",&ret); exit(EXIT_FAILURE); } for(i=0;i<n;i++) for(j=0;j<m;j++) { printf("A[%i,%i] = ",i,j); scanf("%lf",&A[i][j]); } max=0.0; for(i=0;i<n;i++) { sum=0.0; for(j=0;j<m;j++) sum=sum+fabs(A[i][j]); // sum+=fabs(A[i][j]); max=(sum>max?sum:max); // if (sum>max) max=sum; } printf("\nZeilensummennorm = %lf",max); fflush(stdin); for(i=0;i<n;i++) free(*(A+i)); free(A); scanf("%c",&ret); return 0; } 92 Anhang Programmbeispiele in C++ und C Zunächst noch einmal die Zuordnung von einigen Objekten und Standardbezeichnern zu Header-Files. C++ #include <iostream.h> #include <conio.h> #include <stdlib.h> #include <stdlib.h> #include <stdio.h> #include <math.h> // // // // fuer Objekte wie cin, cout,... getch, printf, scanf, stdin,... EXIT_FAILURE,... // koennen sonst entfallen C //#include <iostream.h> // muss entfallen #include <math.h> // falls mathematische Standardfunktionen auftreten //getch, printf, scanf, ... werden automatisch erkannt #include <stdlib.h> #include <stdio.h> // fuer Objekte wie // EXIT_FAILURE,... // stdin, stderr, stdout,... #include <conio.h> // stdin,... enthaelt Header-File stdio.h In den C++-Programmen verwenden wir nicht die Objekte cin, cout, sondern wie in C die Befehle scanf, printf. Neben der zum Teil abweichenden Einbindung von Header-Files in den beiden Sprachdialekten, können noch andere kleine Unterschiede in der Syntax auftreten. So verlangt z. B. C++ bei der Definition von Konstanten zusätzlich die Typangabe. Darüber hinaus gibt es i. Allg. Übereinstimmung zwischen den Versionen. (1) Quadratwurzel // wurzel1.c // Quadratwurzelberechnung sqrt(x) #include <stdio.h> #include <math.h> int main() { int i,n; double xi,x; n = 19; printf("Quadratwurzelberechnung\n"); printf("--------------------------------------------------------------\n"); printf(" i sqrt(i) xi=i,sqrt(xi) x double,sqrt(x)\n"); for(i=1,x=1.0;i<=n;i++,x=x+1.0) { xi = i; printf("%2i %19.16lf %19.16lf %19.16lf\n",i,sqrt(i),sqrt(xi),sqrt(x)); } getch(); return 0; } ------------------------------------------------------------------------------------// wurzel1.cpp // Quadratwurzelberechnung sqrt(x) #include <conio.h> #include <math.h> 93 int main() { int i,n; double xi,x; n = 19; printf("Quadratwurzelberechnung\n"); printf("--------------------------------------------------------------\n"); printf(" i sqrt(i) xi=i,sqrt(xi) x double,sqrt(x)\n"); for(i=1,x=1.0;i<=n;i++,x=x+1.0) { xi = i; printf("%2i %19.16lf %19.16lf %19.16lf\n",i,sqrt(i),sqrt(xi),sqrt(x)); } getch(); return 0; } 2 C:\d\Neundorf\nwptexte\tech phy\05\cpp gpp gcc\wurzel1.exe Quadratwurzelberechnung ————————————————————————————— i sqrt(i) xi=i,sqrt(xi) x double,sqrt(x) 1 1.0000000000000000 1.0000000000000000 1.0000000000000000 2 1.4142135623730951 1.4142135623730951 1.4142135623730951 3 1.7320508075688772 1.7320508075688772 1.7320508075688772 4 2.0000000000000000 2.0000000000000000 2.0000000000000000 5 2.2360679774997898 2.2360679774997898 2.2360679774997898 6 2.4494897427831779 2.4494897427831779 2.4494897427831779 7 2.6457513110645907 2.6457513110645907 2.6457513110645907 8 2.8284271247461903 2.8284271247461903 2.8284271247461903 9 3.0000000000000000 3.0000000000000000 3.0000000000000000 10 3.1622776601683795 3.1622776601683795 3.1622776601683795 ... (2) Kreiszahl π // pi1.c // Kreiszahl Pi durch Integration des Einheitskreises #include <conio.h> #include <math.h> int main() { int i,n; double x,delta_x,pi,Rl,y; const double Pi = 3.1415926535897932; // exakt als Vergleichswert n = 1000; delta_x = 1.0/n; /* Flaeche des Viertelkreises */ Rl = 0.0; for(i=0;i<n;i++) { x = i*delta_x; y = sqrt(1.0-x*x); Rl = Rl+y*delta_x; } pi = 4*Rl; printf("Kreiszahl Pi durch Integration des Einheitskreises: Rechteckregel links\n\n"); printf("Berechneter Wert (Quadratur) Pi = %17.15lf\n",pi); printf("Exakter Wert (16 Stellen) Pi = %17.15lf\n",Pi); // printf("C/C++ : Pi = 17.15%lf",M_PI); // M_PI nicht da getch(); } 94 ---------------------------------------------------------------------------------------------// pi1.cpp, Kreiszahl Pi durch 1.Quadranten des Einheitskreises #include <conio.h> #include <stdlib.h> // kann entfallen #include <math.h> // kann entfallen //#define M_PI 3.14159265358979323846 int main() { int i,n; double x,delta_x,pi,Rl,y; const double Pi = 3.14159265358979323846; ... } // exakt als Vergleichswert 2 C:\d\Neundorf\nwptexte\tech phy\05\cpp gpp gcc\pi1.exe Kreiszahl Pi durch Integration des Einheitskreises: Rechteckregel links Berechneter Wert (Quadratur) Pi = 3.143555466911028 Exakter Wert (16 Stellen) Pi = 3.141592653589793 // pi2.c // Kreiszahl Pi nach Archimedes mittels Umfang von im Kreis einbeschriebenen regelmaessigen Vielecken #include <conio.h> #include <math.h> int main() { int j; double s1,s2,p1,p2,ix; const double Pi = 3.1415926535897932; // exakt als Vergleichswert printf("Pi nach Archimedes\n"); printf(" j s1 Pi1 s2 Pi2\n"); printf("--------------------------------------------------------------------------- \n"); // Initialisierung mit Sechseck s1 = 1; s2 = 1; p1 = 3; p2 = 3; ix = 3; printf("%2i %17.15lf %17.15lf %17.15lf %17.15lf\n",1,s1,p1,s2,p2); fflush(stdin); for(j=2;j<40;j++) { ix = 2*ix; s1 = sqrt(2-sqrt(4-s1*s1)); s2 = s2/sqrt(2+sqrt(4-s2*s2)); p1 = ix*s1; p2 = ix*s2; printf("%2i %17.15lf %17.15lf %17.15lf %17.15lf\n",j,s1,p1,s2,p2); if((j==20)||(j==40)) getch(); } printf("\nExakter Wert (16 Stellen) Pi = %17.15lf\n",Pi); //printf("C/C++ : Pi = 17.15%lf",M_PI); getch(); } -----------------------------------------------------------------------------------------------------// pi2.cpp // Kreiszahl Pi nach Archimedes mittels Umfang von im Kreis einbeschriebenen regelmaesssigen Vielecken #include <conio.h> #include <math.h> //#define M_PI 3.14159265358979323846 int main() { int j; double s1,s2,p1,p2,ix; const double Pi = 3.14159265358979323846; ... } // exakt als Vergleichswert 95 2 C:\d\Neundorf\nwptexte\tech phy\05\cpp gpp gcc\pi2.exe Pi nach Archimedes j s1 Pi1 s2 Pi2 ————————————————————————————————————1 1.000000000000000 3.000000000000000 1.000000000000000 3.000000000000000 2 0.517638090205042 3.105828541230250 0.517638090205041 3.105828541230249 3 0.261052384440103 3.132628613281237 0.261052384440103 3.132628613281238 4 0.130806258460286 3.139350203046872 0.130806258460286 3.139350203046867 5 0.065438165643553 3.141031950890530 0.065438165643552 3.141031950890509 6 0.032723463252972 3.141452472285344 0.032723463252974 3.141452472285462 7 0.016362279207873 3.141557607911622 0.016362279207874 3.141557607911858 8 0.008181208052471 3.141583892148936 0.008181208052470 3.141583892148318 9 0.004090612582340 3.141590463236762 0.004090612582328 3.141590463228050 10 0.002045307360705 3.141592106043048 0.002045307360677 3.141592105999271 11 0.001022653813994 3.141592516588155 0.001022653814027 3.141592516692156 12 0.000511326923607 3.141592618640789 0.000511326923725 3.141592619365383 13 0.000255663463975 3.141592645321216 0.000255663463951 3.141592645033690 14 0.000127831731987 3.141592645321216 0.000127831732237 3.141592651450766 15 0.000063915865994 3.141592645321216 0.000063915866151 3.141592653055036 16 0.000031957932997 3.141592645321216 0.000031957933080 3.141592653456103 17 0.000015978971709 3.141593669849427 0.000015978966540 3.141592653556370 18 0.000007989482381 3.141592303811738 0.000007989483270 3.141592653581437 19 0.000003994762034 3.141608696224804 0.000003994741635 3.141592653587703 20 0.000001997367121 3.141586839655041 0.000001997370818 3.141592653589270 21 0.000000998711352 3.141674265021758 0.000000998685409 3.141592653589662 22 0.000000499355676 3.141674265021758 0.000000499342704 3.141592653589759 23 0.000000249788979 3.143072740170040 0.000000249671352 3.141592653589784 24 0.000000125559416 3.159806164941135 0.000000124835676 3.141592653589791 25 0.000000063220273 3.181980515339464 0.000000062417838 3.141592653589792 26 0.000000033320009 3.354101966249685 0.000000031208919 3.141592653589793 27 0.000000021073424 4.242640687119286 0.000000015604460 3.141592653589793 28 0.000000014901161 6.000000000000000 0.000000007802230 3.141592653589793 29 0.000000000000000 0.000000000000000 0.000000003901115 3.141592653589793 30 0.000000000000000 0.000000000000000 0.000000001950557 3.141592653589793 31 0.000000000000000 0.000000000000000 0.000000000975279 3.141592653589793 32 0.000000000000000 0.000000000000000 0.000000000487639 3.141592653589793 33 0.000000000000000 0.000000000000000 0.000000000243820 3.141592653589793 34 0.000000000000000 0.000000000000000 0.000000000121910 3.141592653589793 35 0.000000000000000 0.000000000000000 0.000000000060955 3.141592653589793 36 0.000000000000000 0.000000000000000 0.000000000030477 3.141592653589793 37 0.000000000000000 0.000000000000000 0.000000000015239 3.141592653589793 38 0.000000000000000 0.000000000000000 0.000000000007619 3.141592653589793 39 0.000000000000000 0.000000000000000 0.000000000003810 3.141592653589793 40 0.000000000000000 0.000000000000000 0.000000000001905 3.141592653589793 41 0.000000000000000 0.000000000000000 0.000000000000952 3.141592653589793 Exakter Wert (16 Stellen) Pi = 3.141592653589793 96 (3) Primzahlen // prim1.c // Die ersten n Primzahlen #include <math.h> int main() { const n = 100; int i,j,k,prim; double is; // auch int const n = 100; i = 1; k = 0; printf("Die ersten n Primzahlen\n"); do { i++; is = sqrt(i); prim = 1; for(j=2;j<=is;j++) if((i%j)==0) // Modulo = Rest der Division { prim = 0; break; } if(prim!=0) { k++; printf("%3i.te Primzahl = %i\n",k,i); } } while(k<n); getch(); } ----------------------------------------------------------------------------------------// prim1.cpp // Die ersten n Primzahlen #include <conio.h> int main() { int const n = 100; int i,j,k,prim; double is; ... } // auch const int n = 100; 2 C:\d\Neundorf\nwptexte\tech phy\05\cpp gpp gcc\prim1.exe Die ersten n Primzahlen 1. Primzahl = 2 2. Primzahl = 3 3. Primzahl = 5 4. Primzahl = 7 5. Primzahl = 11 ... 10. Primzahl = 29 11. Primzahl = 31 ... 26. Primzahl = 101 ... 99. Primzahl = 523 100. Primzahl = 541 97 // prim2.c // Die ersten n Primzahlen als Vektor #include <math.h> int main() { const n = 100; int i,j,k,prim,vprim[n]; i = 2; k = 0; printf("Die ersten n Primzahlen als Vektor\n"); vprim[0] = 2; printf("%3i. Primzahl = %i\n",k+1,i); do { i++; prim = 1; for(j=0;j<k;j++) if((i%vprim[j])==0) { prim = 0; break; } if(prim!=0) { ++k; vprim[k] = i; printf("%3i. Primzahl = %i\n",k+1,i); } } while(k<n-1); getch(); printf("\nAusgabe des Vektors der Primzahlen\n"); for(k=0;k<n;k++) printf("vprim[%3i] = %i\n",k,vprim[k]); getch(); } --------------------------------------------------------------------------// prim3.c // Die ersten n Primzahlen als Vektor #include <math.h> int main() { const n = 10; int i,j,k,prim,is,vprim[n+1]; i = vprim[0] = 1; k = 2; vprim[1] = 2; vprim[2] = 3; printf("Die ersten n Primzahlen als Vektor\n"); printf("%3i. Primzahl = %i\n",1,vprim[1]); do { i = i+2; is = (int)sqrt(i); // Typumwandlung prim = 1; j = 2; while((vprim[j]<=is)&&(prim!=0)) { prim = i%vprim[j]; j++; } if(prim!=0) printf("%3i. Primzahl = %i\n",k++,vprim[k]=i); } while(k<=n); getch(); printf("\nAusgabe des Vektors der Primzahlen\n"); for(k=1;k<=n;k++) printf("vprim[%3i] = %i\n",k,vprim[k]); getch(); } 98 2 C:\d\Neundorf\nwptexte\tech phy\05\cpp gpp gcc\prim3.exe Die ersten n Primzahlen 1. Primzahl = 2 2. Primzahl = 3 3. Primzahl = 5 ... 10. Primzahl = 29 Ausgabe des Vektors der Primzahlen vprim[ 1] = 2 vprim[ 2] = 3 vprim[ 3] = 5 ... vprim[ 10] = 29 // prim4.c // Alle Primzahlen <= n, Sieb des Eratosthenes #include <math.h> int main() { const nmax = 1000; int i,k,n,ns,vprim[nmax+1]; printf("Sieb des Eratosthenes: alle Primzahlen <= n\n"); do { printf("n (2<=n<=%i) = ",nmax); scanf("%i",&n); } while((n<2)||(n>nmax)); vprim[0] = 0; vprim[1] = 1; for(i=2;i<=n;i++) vprim[i]=i; ns = (int)sqrt(n); for(i=2;i<=ns;i++) if(vprim[i]!=0) { k = 2*i; while(k<=n) { vprim[k] = 0; k = k+i; } } printf("\nPrimzahlen <= %i\n",n); for(i=2;i<=n;i++) { if(vprim[i]!=0) printf(" %i",i); if(i%30==0) getch(); } getch(); } 2 C:\d\Neundorf\nwptexte\tech phy\05\cpp gpp gcc\prim4.exe Sieb des Eratosthenes: alle Primzahlen <= n n (2<=n<=1000) = 100 Primzahlen <= 100 2 3 5 7 11 13 17 19 79 83 89 97 31 37 41 43 99 47 53 59 61 71 73 // prim5.c // Primzahltest mittels ganzzahliger Division #include <math.h> int main() { int n,ns,teiler,kleinster_teiler,prim; printf("Primzahltest mittels ganzzahliger Division\n"); do { printf("Zahl n (n>=2) = "); scanf("%i",&n); } while(n<=1); prim = 1; teiler = 2; kleinster_teiler = 2; ns = (int)sqrt(n); while(teiler<=ns) { if((n%teiler==0)&&(n>3)) { prim = 0; kleinster_teiler = teiler; break; } teiler++; } if(prim) printf("\n%i ist Primzahl",n); else { printf("\n%i ist keine Primzahl",n); printf("\nKleinster Teiler = %i",kleinster_teiler); } getch(); } ---------------------------------------------------------------------------------------------// prim5.cpp // Primzahltest mittels ganzzahliger Division #include <conio.h> int main() { int n,ns,teiler,kleinster_teiler,prim; ... } 2 C:\d\Neundorf\nwptexte\tech phy\05\cpp gpp gcc\prim5.exe Primzahltest mittels ganzzahliger Division Zahl n (n>=2) = 93 93 ist keine Primzahl Kleinster Teiler = 3 ————————————————————————————————————— Primzahltest mittels ganzzahliger Division Zahl n (n>=2) = 107 107 ist Primzahl 100 (4) Summenberechnungen // summe1_gauss.c // Summe 1+2+...+n=n(n+1)/2 Gauss-Formel main() { int i,n,s,g; n = 100; s = 0; for(i=1;i<=n;i=i+1) s = s+i; // s+=i; g = n*(n+1)/2; // Ergebnis ist ganzzahlig printf("Gauss-Formel 1+2+...+n=n(n+1)/2\n"); printf("n=%i\n",n); printf("g=n(n+1)/2 = %i\n",g); printf("s=0+1+2+...+n = %i\n",s); getch(); } ---------------------------------------------------------------------// summe1_gauss.cpp // Summe 1+2+...+n=n(n+1)/2 Gauss-Formel #include <conio.h> main() { int i,n,s,g; ... } 2 C:\d\Neundorf\nwptexte\tech phy\05\cpp gpp gcc\summe1 gauss.exe Gauss-Formel 1+2+...+n=n(n+1)/2 n=100 g=n(n+1)/2 = 5050 s=0+1+2+...+n = 5050 // summe2_geom.c // Summe 2=1+1/2+1/2^2+1/2^3+1/2^4+.... geometrische Reihe int main() { int i,n; double s,a; n = 16; a = 1.0; s = 1.0; for(i=1;i<=n;i=i+1) { a = a/2.0; s = s+a; } printf("Geometrische Reihe 2=1+1/2+1/2^2+1/2^3+1/2^4+....\n"); printf("Partialsumme s(n)=s(%i)=%14.12lf\n",n,s); getch(); return 0; } 2 C:\d\Neundorf\nwptexte\tech phy\05\cpp gpp gcc\summe2 geom.exe Geometrische Reihe 2=1+1/2+1/2∧ 2+1/2∧ 3+1/2∧ 4+.... Partialsumme s(n)=s(16)=1.999984741211 101 // summe3_sin.c // Summe sin(x)=x-x^3/3!+x^5/5!-x^7/7!+x^9/9!-... Potenzreihe #include <math.h> int main() { int i,n; double x,x2,s,a; x = 1.5; x2 = x*x; n = 15; a = x; s = x; for(i=2;i<=n;i=i+2) { a = -a*x2/(i*(i+1)); s = s+a; } printf("Potenzreihe sin(x)=x-x^3/3!+x^5/5!-x^7/7!+x^9/9!-...\n"); printf("sin(%lf)=%17.15lf\n",x,sin(x)); printf("Partialsumme s(n)=s(%i)=%17.15lf\n",n,s); getch(); return 0; } 2 C:\d\Neundorf\nwptexte\tech phy\05\cpp gpp gcc\summe3 sin.exe Potenzreihe sin(x)=x-x∧ 3/3!+x∧ 5/5!-x∧ 7/7!+x∧ 9/9!-... sin(1.500000)=0.997494986604054 Partialsumme s(n)=s(15)=0.997494986601303 // summe4_sin.c // Summe sin(x)=x-x^3/3!+x^5/5!-x^7/7!+x^9/9!-... Potenzreihe // nach Transformation von x auf das Intervall [-Pi,Pi] #include <math.h> int main() { const double Pi = 3.1415926535897932; int i,n,Q,h,vz; double xe,x,x2,sinx,xt,s,a; printf("Potenzreihe sin(x)=x-x^3/3!+x^5/5!-x^7/7!+x^9/9!-...\n"); printf("nach Transformation von x auf das Intervall [-Pi,Pi]\n\n"); n = 100; vz = 1; printf("x = "); scanf("%lf",&xe); /*Transformation in den Bereich von -Pi bis +Pi*/ x = xe; if(x<0) { x = -x; vz = -1; } Q = (int)(x/(0.5*Pi)); h = Q%2; // Rest der ganzzahligen Division xt = (x-2.0*Pi*(Q/4+(Q/2)%2))*(1-2*h*h)+Pi*h; if(xt>Pi) xt = xt-2*Pi; /*Berechnung von sin(x)*/ x2 =xt*xt; for(i=0,sinx=0.0,s=a=xt;(sinx!=s)&&(i<n);i=i+1) s=(sinx=s)+(a=-a*x2/((2*i+2)*(2*i+3))); sinx = vz*sinx; 102 printf("\nErgebnisse\n"); printf("x =%19.15lf\n",xe); printf("xt=%19.15lf\n\n",xt); printf("sin(%19.15lf)=%19.15lf\n",xe,sin(xe)); printf("Partialsumme s(n)=s(%i)=%19.15lf\n",n,sinx); getch(); return 0; } 2 C:\d\Neundorf\nwptexte\tech phy\05\cpp gpp gcc\summe4 sin.exe Potenzreihe sin(x)=x-x∧ 3/3!+x∧ 5/5!-x∧ 7/7!+x∧ 9/9!-... nach Transformation von x auf das Intervall [-Pi,Pi] x = 0.5 Ergebnisse x = 0.500000000000000 xt = 0.500000000000000 sin( 0.500000000000000)= 0.479425538604203 Partialsumme s(n)=s(100)= 0.479425538604203 ————————————————————————————————————x = 2.2 Ergebnisse x = 2.200000000000000 xt = 0.941592653589793 sin( 2.200000000000000)= 0.808496403819590 Partialsumme s(n)=s(100)= 0.808496403819590 ————————————————————————————————————x = 3.5 Ergebnisse x = 3.500000000000000 xt = -2.783185307179586 sin( 3.500000000000000)= -0.350783227689620 Partialsumme s(n)=s(100)= -0.350783227689620 ————————————————————————————————————x=6 Ergebnisse x = 6.000000000000000 xt = -2.858407346410207 sin( 6.000000000000000)= -0.279415498198926 Partialsumme s(n)=s(100)= -0.279415498198926 ————————————————————————————————————x = 100 Ergebnisse x =100.000000000000000 xt = -2.610627738716413 sin( 100.000000000000000)= -0.506365641109759 Partialsumme s(n)=s(100)= -0.506365641109759 103 // summe5_sin.c // Summe sin(x)=x-x^3/3!+x^5/5!-x^7/7!+x^9/9!-... Potenzreihe // nach Transformation von x auf das Intervall [0,Pi/2] #include <math.h> int main() { const double Pi = 3.1415926535897932; int i,n,vz,ix,vorz; double xe,x,x2,sinx,s,a; printf("Potenzreihe sin(x)=x-x^3/3!+x^5/5!-x^7/7!+x^9/9!-...\n"); printf("nach Transformation von x auf das Intervall [0,Pi/2]\n\n"); n = 100; vz = 1; vorz = 1; printf("x = "); scanf("%lf",&xe); /*Transformation in den Bereich von 0 bis 2*Pi*/ x = xe; if(x<0) { x = -x; vz = -1; } ix = (int)(x/(2*Pi)); // wie oft ist 2*Pi in x? x = x-ix*2*Pi; /* Transformation des if(x>=1.5*Pi) // { vorz = -1; x = 2*Pi-x; } else if(x>=Pi) // { vorz = -1; x = x-Pi; } else if(x>=0.5*Pi) // x = Pi-x; Argumentes in [0,Pi/2), Symmetrien ausnutzen */ 4. Quadrant 3. Quadrant 2. Quadrant /*Berechnung von sin(x)*/ x2 =x*x; for(i=0,sinx=0.0,s=a=x;(sinx!=s)&&(i<n);i=i+1) s = (sinx=s)+(a=-a*x2/((2*i+2)*(2*i+3))); sinx = vz*vorz*sinx; printf("\nErgebnisse\n"); printf("x =%19.15lf\n",xe); printf("xt=%19.15lf\n\n",x); printf("sin(%19.15lf)=%19.15lf\n",xe,sin(xe)); printf("Partialsumme s(n)=s(%i)=%19.15lf\n",n,sinx); getch(); return 0; } ----------------------------------------------------------------------------------// summe5_sin.cpp // Summe sin(x)=x-x^3/3!+x^5/5!-x^7/7!+x^9/9!-... Potenzreihe // nach Transformation von x auf das Intervall [0,Pi/2] #include <conio.h> int main() { const double Pi = 3.1415926535897932; int i,n,vz,ix,vorz; double xe,x,x2,sinx,s,a; ... } 104 2 C:\d\Neundorf\nwptexte\tech phy\05\cpp gpp gcc\summe5 sin.exe Potenzreihe sin(x)=x-x∧ 3/3!+x∧ 5/5!-x∧ 7/7!+x∧ 9/9!-... nach Transformation von x auf das Intervall [-Pi,Pi] x = 0.5 Ergebnisse x = 0.500000000000000 xt = 0.500000000000000 sin( 0.500000000000000)= 0.479425538604203 Partialsumme s(n)=s(100)= 0.479425538604203 ————————————————————————————————————x = 2.2 Ergebnisse x = 2.200000000000000 xt = 0.941592653589793 sin( 2.200000000000000)= 0.808496403819590 Partialsumme s(n)=s(100)= 0.808496403819590 ————————————————————————————————————x = 3.5 Ergebnisse x = 3.500000000000000 xt = 0.358407346410207 sin( 3.500000000000000)= -0.350783227689620 Partialsumme s(n)=s(100)= -0.350783227689620 ————————————————————————————————————x=6 Ergebnisse x = 6.000000000000000 xt = 0.283185307179586 sin( 6.000000000000000)= -0.279415498198926 Partialsumme s(n)=s(100)= -0.279415498198926 ————————————————————————————————————x = 100 Ergebnisse x =100.000000000000000 xt = 0.503964914873373 sin( 100.000000000000000)= -0.506365641109759 Partialsumme s(n)=s(100)= -0.506365641109759 ————————————————————————————————————x = -99 Ergebnisse x = -99.000000000000000 xt = 1.530964914873373 sin( -99.000000000000000)= 0.999206834186354 Partialsumme s(n)=s(100)= 0.999206834186353 105 (5) Integration, Quadraturformeln // integrate1.c // Zusammengesetzte Newton-Cotes-Formel: Trapezregel, Simpson-Regel #include <math.h> // Integrand double f(double x) { return exp(x); } // Zusammengesetzte Trapezregel, Integrand global double trapez_z(double a,double b,int n) { int i; double h,s1; h = (b-a)/n; s1 = 0.5*(f(a)+f(b)); for(i=1;i<=n-1;i++) s1 = s1+f(a+i*h); return s1*h; } // Zusammengesetzte Simpson-Regel, Integrand global double simpson_z(double a,double b,int n) { int i; double h,s1; h = (b-a)/(2*n); s1 = f(a)+f(b); i = 2; while(i<=(2*n-2)) { s1 = s1+2*f(a+i*h); i = i+2; } i = 1; while(i<=(2*n-1)) { s1 = s1+4*f(a+i*h); i = i+2; } return s1*h/3; } int main() { int n; double a,b,T1,T2,S; a = 0.0; // linke und rechte Intervallgrenze b = 1.0; n = 10; // Anzahl der Integrationsintervalle printf("Bestimmtes Integral Int(f(x),x=a..b)\n"); printf("Numerische Integration mittels Newton-Cotes-Quadraturformeln\n"); printf("Zusammengesetzte Trapezregel, Simpson-Regel\n\n"); printf("Int(exp(x),x=0..1) = exp(1)-1 = %18.15lf\n",exp(1.0)-1.0); printf("Anzahl der Teilintervalle n = %i\n",n); T1 = trapez_z(a,b,n); printf("T_z(n) = %12.9lf\n",T1); T2 = trapez_z(a,b,2*n); printf("T_z(2n) = %12.9lf\n",T2); S = simpson_z(a,b,n); printf("S_z(n) = %12.9lf\n",S); printf("\nKontrolle S_z(n)=(4T_z(2n)-T_z(n))/3 \n"); printf("(4T_z(2n)-T_z(n))/3 = %12.9lf\n",(4*T2-T1)/3); getch(); } 106 2 C:\d\Neundorf\nwptexte\tech phy\05\cpp gpp gcc\integrate1.exe Bestimmtes Integral Int(f(x),x=a..b) Numerische Integration mittels Newton-Cotes-Quadraturformeln Zusammengesetzte Trapezregel, Simpson-Regel Int(exp(x),x=0..1) = exp(1)-1 = 1.718281828459045 Anzahl der Teilintervalle n = 10 T z(n) = 1.719713491 T z(2n) = 1.718639789 S z (n) = 1.718281888 Kontrolle S z(n)=(4T z(2n)-T z(n))/3 (4T z(2n)-T z(n))/3 = 1.718281888 // integrate2.c // Duale adaptive Trapezregel mit Grob- und Feinrechnung #include <math.h> // globale Groessen int anz; double hmin; // Definition des Integranden double f(double x) { return 1.0/(1e-4+x*x); // andere Integranden // return sin(x); // return sqrt(sqrt(sqrt(fabs(x)))); } // Bestimmtes Integral mit dualer adaptiver Trapezregel void Integral(double x1,double x2,double f1,double f2,double I,double h,double eps,double *s) // hmin,anz,f global { double I1,I2,I12,xm,fm; xm = (x1+x2)*0.5; fm = f(xm); anz++; h = 0.5*h; if(h<hmin) hmin = h; I1 = 0.5*h*(f1+fm); I2 = 0.5*h*(fm+f2); I12 = I1+I2; if(fabs(I-I12)<eps*(fabs(I)+eps)) (*s) = (*s)+I12; else { Integral(x1,xm,f1,fm,I1,h,eps,s); // Integral(x1,xm,f1,fm,I1,h,eps,&*s); Integral(xm,x2,fm,f2,I2,h,eps,s); } } int main() { double a,b,eps,fa,fb,h,s,I; printf("Bestimmtes Integral Int(f(x),x=a..b)\n"); printf("Duale adaptive Trapezregel mit Grob- und Feinrechnung\n\n"); printf("Untere Grenze a = "); scanf("%lf",&a); printf("Obere Grenze b = "); scanf("%lf",&b); printf("Genauigkeit eps = "); scanf("%lf",&eps); printf("\n"); 107 // Vorbereitung des rekursiven Prozeduraufrufes hmin = b-a; fa = f(a); fb = f(b); anz = 2; h = b-a; s = 0.0; I = 0.5*h*(fa+fb); Integral(a,b,fa,fb,I,h,eps,&s); printf("Integralwert s = %15.12lf\n",s); printf("Anzahl der FW-Auswertungen anz = %i\n",anz); printf("Minimaler Knotenabstand hmin = %11.9lf\n",hmin); getch(); } Beispiel Z1 dx = 200 arctan(100) = 312.159 332 021 646 276 20. + x2 10−4 −1 2 C:\d\Neundorf\nwptexte\tech phy\05\cpp gpp gcc\integrate2.exe Bestimmtes Integral Int(f(x),x=a..b) Duale adaptive Trapezregel mit Grob- und Feinrechnung Untere Grenze a = -1 Obere Grenze b = 1 Genauigkeit eps = 1e-6 Integralwert s = 312.159350642377 Anzahl der FW-Auswertungen anz = 16097 Minimaler Knotenabstand hmin = 0.000007629 ————————————————————————————————————Genauigkeit eps = 1e-9 Integralwert s = 312.159332039934 Anzahl der FW-Auswertungen anz = 509777 Minimaler Knotenabstand hmin = 0.000000238 ————————————————————————————————————Genauigkeit eps = 1e-12 Integralwert s = 312.159332021675 Anzahl der FW-Auswertungen anz = 16137201 Minimaler Knotenabstand hmin = 0.000000007 ————————————————————————————————————Genauigkeit eps = 1e-13 Integralwert s = 312.159332021643 Anzahl der FW-Auswertungen anz = 51898825 Minimaler Knotenabstand hmin = 0.000000004 ————————————————————————————————————Genauigkeit eps = 1e-14 Integralwert s = 312.159332021827 Anzahl der FW-Auswertungen anz = 156574921 Minimaler Knotenabstand hmin = 0.000000001 Die letzten Rechnungen mit sehr kleiner Toleranz dauern im Sekundenbereich. Der Fehler wird auf Grund der extrem vielen Schritte dabei wieder zunehmen (Fehlerakkumulation). 108 // integrate3.c // Zusammengesetzte Trapezregel mit Grob- und Feinrechnung sowie Intervallhalbierung #include <math.h> // Prototypen double integrate1(double(*funktion)(double),double,double,double); double integrate2(double(*funktion)(double),double,double,double); double myf(double); int main() { double a,b,eps,I; a = 0.0; b = 1.0; eps = 1e-6; printf("Bestimmtes Integral Int(f(x),x=a..b)\n"); printf("Zusammengesetzte Trapezregel mit Grob- und Feinrechnung sowie Intervallhalbierung\n\n"); I = integrate1(myf,a,b,eps); printf("While-Schleife: I1 = %12.9lf\n",I); I = integrate2(myf,a,b,eps); printf("Do-While-Schleife: I2 = %12.9lf\n",I); getch(); } /*---------------------------------------------------------------------------*/ double integrate1(double(*funktion)(double x),double a,double b,double eps) { double Ialt,Ineu,xi,dx,diff; int i,N; /* Erste Naeherung, einfache Trapezregel */ Ialt = (b-a)*0.5*((*funktion)(a)+(*funktion)(b)); /* Zweite Naeherung */ N = 2; dx = (b-a)/N; xi = a+dx; Ineu = dx*0.5*((*funktion)(a)+2*(*funktion)(xi)+(*funktion)(b)); /* Iteration mit Grob- und Feinrechnung */ diff = fabs(Ineu-Ialt); while(diff>eps) // While-Schleife { Ialt = Ineu; N = N*2; dx = (b-a)/N; Ineu = 0.0; for(i=0;i<N;i++) { xi = a+i*dx; Ineu = Ineu+dx*0.5*((*funktion)(xi)+(*funktion)(xi+dx)); } diff = fabs(Ineu-Ialt); } return Ineu; } double integrate2(double(*funktion)(double x),double a,double b,double eps) { double Ialt,Ineu,xi,dx,diff; int i,N; /* Erste Naeherung, einfache Trapezregel ohne Intervallbreite*/ Ineu = 0.5*((*funktion)(a)+(*funktion)(b)); N = 1; /* Iteration mit Grob- und Feinrechnung */ do // Do-While-Schleife { Ialt = Ineu; N = N*2; dx = (b-a)/N; for(i=1;i<N;i=i+2) { 109 xi = a+i*dx; Ineu = Ineu+(*funktion)(xi); // rekursive Berechnung // damit weniger FW-Berechnungen } diff = fabs(Ineu*dx-Ialt*2.0*dx); } while(diff>eps); return Ineu*dx; } /*---------------------------------------------------------------------------*/ double myf(double x) { return x*x; // Normalparabel } Beispiel Zb x2 dx = 1 3 (b − a3 ), a = 0, b = 1, ε = 10−6 . 3 a 2 C:\d\Neundorf\nwptexte\tech phy\05\cpp gpp gcc\integrate3.exe Bestimmtes Integral Int(f(x),x=a..b) Zusammengesetzte Trapezregel mit Grob- und Feinrechnung sowie Intervallhalbierung While-Schleife: I1 = 0.333333492 Do-While-Schleife: I2 = 0.333333492 // // // // // integrate4.c Vergleich verschiedener Integrationsmethoden fuer Integrand = Polynom - Zusammengesetzte Trapezregel - Monte-Carlo-Methode - exaktes Verfahren mit Stammfunktion #include <stdlib.h> #include <math.h> #include <time.h> #define maxgrad 4 // maximaler Grad des Polynoms // Prototypen double direkt(double,double); double Trapezregel(double(*funktion)(double),double,double,double); double Monte_Carlo(double(*funktion)(double),double,double,double); double polynom(double); /* Globale Variablen */ double ai[maxgrad+1]; // Koeffizienten des Polynoms int grad; // Grad des Polynoms /*---------------------------------------------------------------------------*/ int main() { double a,b,eps,Id,It,Im; int i,NN,td,tt,tm; time_t ta,te; printf("Berechnung des bestimmten Integrals eines Polynoms Int(pn(x),x=a..b)\n"); printf("Das Integral wird NN mal berechnet, um Laufzeitunterschiede zu ermitteln.\n\n"); printf("Eingaben\n"); printf("Untere Grenze a = "); scanf("%lf",&a); printf("Obere Grenze b = "); scanf("%lf",&b); printf("Genauigkeit eps = "); scanf("%lf",&eps); printf("\nPolynom\n"); do { printf("Grad des Polynoms (max. %i. Grades) : ",maxgrad); 110 scanf("%i",&grad); } while ((grad<0)||(grad>maxgrad)); printf("Polynomkoeffizienten a[i] von pn(x)=a[0]+a[1]x+...+a[n]x^n\n"); for(i=0;i<=grad;i++) { printf("a[%i] = ",i); scanf("%lf",&ai[i]); } for(i=grad+1;i<=maxgrad;i++) ai[i] = 0.0; // mit Zeitmessung, N hinreichend gross waehlen fuer Zeit in sec NN = 100; printf("\nBerechnungen laufen\n"); printf("Anzahl der Durchlaeufe NN = %i\n\n",NN); ta = time(NULL); for(i=1;i<=NN;i++) Id = direkt(a,b); te = time(NULL); td = te-ta; ta = time(NULL); for(i=1;i<=NN;i++) It = Trapezregel(polynom,a,b,eps); te = time(NULL); tt = te-ta; ta = time(NULL); for(i=1;i<=NN;i++) Im = Monte_Carlo(polynom,a,b,eps); te = time(NULL); tm = te-ta; printf("Integrationsmethoden\n"); printf("Direkt Id = %12.9lf printf("Tapezregel It = %12.9lf printf("Monte-Carlo Im = %12.9lf getch(); Zeit in sec = %ld\n",Id,td); Zeit in sec = %ld\n",It,tt); Zeit in sec = %ld\n",Im,tm); } /*---------------------------------------------------------------------------*/ double direkt(double a,double b) { int i; double I; I = 0.0; for(i=0;i<=grad;i++) I = I+ai[i]/(i+1)*(pow(b,i+1)-pow(a,i+1)); return I; } /*---------------------------------------------------------------------------*/ double Trapezregel(double (*funktion)(double x),double a,double b,double eps) { int i,N; double Ialt,Ineu,xi,dx; // Erste Naeherung, einfache Trapezregel Ineu = 0.5*((*funktion)(a)+(*funktion)(b)); N = 1; // Iteration do { Ialt = Ineu; N = N*2; dx = (b-a)/N; for(i=1;i<N;i=i+2) { xi = a+i*dx; Ineu = Ineu+(*funktion)(xi); } } while(fabs(Ineu*dx-Ialt*2.0*dx)>eps); return Ineu*dx; } /*---------------------------------------------------------------------------*/ double Monte_Carlo(double(*funktion)(double x),double a,double b,double eps) { const int N = 100; 111 int i,anzp; double fmittel,fmittelalt,xr,r; //srand48(1357); // Zufallszahlengenerator initialisieren srand(1357); r = (double)RAND_MAX; fmittel = 0.0; for(i=1;i<=N;i++) { //xr = drand48(); xr = a+(b-a)*rand()/r; fmittel = fmittel+(*funktion)(xr); } anzp = N; do { fmittelalt = fmittel; for(i=1;i<=N;i++) { //xr = drand48(); xr = a+(b-a)*rand()/r; fmittel = fmittel+(*funktion)(xr); } anzp = anzp+N; // printf("I: %lf AP: %i\n", fmittel/anzp*(b-a), anzp); } while(fabs(fmittel/anzp-fmittelalt/(anzp-N))*(b-a)>eps); return fmittel/anzp*(b-a); } /*---------------------------------------------------------------------------*/ double polynom(double x) { int i; double p; p = 0.0; for(i=0;i<=grad;i++) p = p+ai[i]*pow(x,i); return p; } ---------------------------------------------------------------------------------------// // // // // integrate4.cpp Vergleich verschiedener Integrationsmethoden fuer Integrand = Polynom - Zusammengesetzte Trapezregel - Monte-Carlo-Methode - exaktes Verfahren mit Stammfunktion #include <stdlib.h> #include <conio.h> #include <math.h> #include <time.h> #define maxgrad 4 // maximaler Grad des Polynoms // Prototypen double direkt(double,double); double Trapezregel(double(*funktion)(double),double,double,double); double Monte_Carlo(double(*funktion)(double),double,double,double); double polynom(double); /* Globale Variablen */ double ai[maxgrad+1]; // Koeffizienten des Polynoms int grad; // Grad des Polynoms /*---------------------------------------------------------------------------*/ int main() { double a,b,eps,Id,It,Im; int i,NN,td,tt,tm; time_t ta,te; ... } ... 112 Beispiel Zb pn (x) dx = a Zb (a0 + a1 x + ... + an xn ) dx a ib 1 1 a0 x + a1 x2 + ... + an xn+1 2 n+1 a n X 1 = ai (bi+1 − ai+1 ). i + 1 i=0 = h 2 C:\d\Neundorf\nwptexte\tech phy\05\cpp gpp gcc\integrate4.exe Berechnung des bestimmten Integrals eines Polynoms Int(pn(x),x=a..b) Das Integral wird NN mal berechnet, um Laufzeitunterschiede zu ermitteln. Eingaben Untere Grenze a = 0 Obere Grenze b = 1 Genauigkeit eps = 1e-6 Polynom Grad des Polynoms (max. 4. Grades) : 3 Polynomkoeffizienten a[i] von pn(x)=a[0]+a[1]x+...+a[n]x∧ n a[0] = 1 a[1] = 1 a[2] = 1 a[3] = 1 Berechnungen laufen Anzahl der Durchlaeufe NN = 100 Integrationsmethoden Direkt (exakt) Id = 2.083333333 Zeit in sec = 0 (NN=200: 0, NN=400: 0, NN=600: 0) Tapezregel It = 2.083333433 Zeit in sec = 0 ( 0, 1, 1) Monte-Carlo Im = 2.072050397 Zeit in sec = 1 ( 3, 6, 8) ———————————————————————————————————————– ... Genauigkeit eps = 1e-8 ... Anzahl der Durchlaeufe NN = 100 Integrationsmethoden Direkt (exakt) Id = 2.083333333 Tapezregel It = 2.083333335 Monte-Carlo Im = 2.080377829 Zeit in sec = 0 (NN=200: 0, NN=400: 0, NN=600: 0) Zeit in sec = 1 ( 3, 6, 9) Zeit in sec = 24 ( 46, 95, 140) Das Verfahren von Monte-Carlo ist im Vergleich mit Abstand am langsamsten und sehr ungenau. 113