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

Similar documents