Musterlösung zum Übungsblatt Nr. 4

Musterlösung zur Aufgabe 1 (7.5 Punkte)

Die Berechnung des Schnittpunktes der Funktion $g(x)=x^3$ mit der Funktion $h(x)=$ln$(x)+5$ ist gleichbedeutend mit der Nullstellensuche der Funktion $f(x) := h(x) - g(x) = $ ln$(x) + 5 - x^3$. Das folgende C++ Programm stellt eine Musterlösung zur Aufgabe 1 dar:

A4_1.cpp
// Musterlösung der Aufgabe 1 des Übungsblattes Nr.4
/* Methode der Bisektion (Intervallhalbierungsverfahren)
 * Berechnung des Schnittpunktes der Funktionen g(x)=x^3 und h(x)=ln(x)+5 im Intervall [0.25,2.5]
 * entspricht einer Nullstellensuche der Funktion f(x)=ln(x)+5-x^3
 * Ausgabe zum Plotten (Gnuplot oder Python) mittels: "./a.out > A4_1.dat" */
#include <iostream>                                          // Ein- und Ausgabebibliothek
#include <cmath>                                             // Bibliothek für mathematisches (e-Funktion, Betrag, ...)

double f (double x) {                                        // Deklaration und Definition der Funktion f(x)
    double wert;                                             // Lokale double-Variable (nur im Bereich der Funktion gültig)
    wert = log(x) + 5 - pow(x,3);                            // Eigentliche Definition der Funktion
    return wert;                                             // Rueckgabewert der Funktion f(x)
}                                                            // Ende der Funktion f(x)

int main(){                                                  // Hauptfunktion
    int i;                                                   // Deklaration des Laufindex als ganze Zahl
    double a = 0.25;                                         // Untergrenze des Intervalls [a,b] in dem f(x)=0 gelten soll
    double b = 2.5;                                          // Obergrenze des Intervalls [a,b] in dem f(x)=0 gelten soll
    const int N = 10;                                        // Anzahl der Iterationen im Intervallhalbierungsverfahren
    double p, fp;                                            // Deklaration der Variablen des approximierten x- und f(x)-Wertes der Nullstelle
    double fa=f(a);                                          // Deklaration und Initialisierung des Funktionswertes f(a) an der Untergrenze
    const double Loes = 1.7729093296693715;                  // Wirklicher Wert des Schnittpunktes
    
    printf("# 0: Index i der Iteration \n# 1: Approximierter Wert der Nullstelle p_i \n");          // Beschreibung der ausgegebenen Groessen
    printf("# 2: Funktionswert f(p_i) \n# 3: Relativer Fehler zum wirklichen Wert |(p-p_i)/p| \n"); // Beschreibung der ausgegebenen Groessen
    printf("# 4: Untergrenze a \n# 5: Obergrenze b \n");                                            // Beschreibung der ausgegebenen Groessen
    
    for(i=0; i<=N; ++i){                                     // Schleifen Anfang
        p = a + (b-a)/2;                                     // Festlegung des x-Wertes in der Mitte des Intervalls [a,b] (x=p)
        fp = f(p);                                           // Berechnung des f(p)-Wertes in der Mitte des Intervalls [a,b]
        
        printf("%3d %14.10f %14.10f %14.10f %14.10f %14.10f \n",i, p, fp, fabs((Loes-p)/Loes), a, b); // Ausgabe der berechneten Werte
        
        if( fa*fp > 0 ){                                     // Falls der berechnete Wert f(p) das gleiche Vorzeichen hat wie f(a), dann ...
            a = p;                                           // ... setze die Untergrenze auf a=p und ...
            fa = fp;                                         // ... lege den neuen Funktionswert an der Untergrenze fest: f(a)=f(p)
        } else if( fa*fp < 0 ){                              // Falls der berechnete Wert f(p) ein unterschiedliches Vorzeichen hat wie f(a), dann ...
            b = p;                                           // ... setze die Obergrenze auf b=p
        } else{                                              // Falls durch der berechnete Funktionswert gerade Null ist (f(p)=0 -> f(a)*f(p)=0), dann ...
            i = N;                                           // ... beende das Bisektion-Verfahren vorzeitig
        }                                                    // else Ende
    }                                                        // Ende der for-Schleife des Bisektion-Verfahres
}

Das initiale Intervall $[0.25,2.5]$ wird mittels der Methode der Bisektion in jedem Iterationsschritt halbiert und die untere linke Abbildung zeigt die Terminalausgabe des ausgeführten Programms.

Man erkennt schon hier, dass der approximierte Wert der Nullstelle der Funktion $f(x)$ nach 10 Iterationen gut mit dem Literaturwert des x-Wertes des Schnittpunktes ($x = 1.7729093296693715...$) übereinstimmt. Die nebenstehende linke Abbildung verdeutlicht den Algorithmus der Intervallhalbierung in unserem Beispiel. Die Abbildung wurde mittels des unten angegebenen Python-Skriptes erstellt, wobei zunächst die Terminalausgabe des C++ Programms in eine Datei umgeleitet wurde (' ./a.out > A4_1.dat ').

PythonPlot_A4_1.py
# Python Programm zum Plotten der berechneten Daten von (A4_1.cpp)
import matplotlib.pyplot as plt                                                                # Python Bibliothek zum Plotten (siehe https://matplotlib.org/ )
import numpy as np                                                                             # Python Bibliothek fuer Mathematisches (siehe https://numpy.org/ )

data = np.genfromtxt("./A4_1.dat")                                                             # Einlesen der berechneten Daten von A4_1.cpp

plt.title(r'Methode der Bisektion (Intervallhalbierungsverfahren)')                            # Titel der Abbildung
plt.ylabel(r'$g(x)=x^3 \, , \,\, h(x)=ln(x)+5$')                                               # Beschriftung y-Achse
plt.xlabel('x')                                                                                # Beschriftung x-Achse

x_interval=np.linspace(data[0,4], data[0,5], 200)                                              # x-Intervall [a,b] als Liste von 200 Zahlen
plt.plot(x_interval,x_interval**3, color="blue", label="g(x)")                                 # Plotten der Funktion f(x) im Intervall [a,b]
plt.plot(x_interval,np.log(x_interval)+5, color="green", label="h(x)")                         # Plotten der Funktion f(x) im Intervall [a,b]

y_interval=np.linspace(x_interval[-1]**3, data[-1,1]**3, int(data[-1,0])+1)                    # y-Bereich zum Veranschaulichen der unterschiedlichen Intervalle

for i in data[:,0]:                                                                            # Schleife um die einzelnen Resultate der Nullstelle Bisektion zu plotten
    index=int(i)                                                                               # Umwandlung des Laufindex in eine Integer Zahl
    plt.plot([data[index,4],data[index,5]],[y_interval[index],y_interval[index]], color="red") # Plotten des i-ten Intervalls
    plt.scatter(data[index,1],y_interval[index], marker='*', color="black", s=25)              # Plotten des i-ten p-Wertes
    
plt.legend(frameon=True, loc="lower right",fontsize=10)                                        # Anordnung der Legende

plt.savefig("A4_1.png", bbox_inches="tight")                                                   # Speichern der Abbildung als Bild
plt.show()                                                                                     # Zusaetzliches Darstellen der Abbildung in einem separaten Fenster



Musterlösung zur Aufgabe 2 (7.5 Punkte)

Mittels des folgenden C++ Programms wurde zunächst die (x-y)-Wertetabelle, der vom Benutzer spezifizierten Funktion, ausgegeben.

A4_2.cpp
// Musterlösung der Aufgabe 2 des Übungsblattes Nr.4
#include <iostream>      // Ein- und Ausgabebibliothek
#include <cmath>         // Bibliothek für mathematisches (e-Funktion, Betrag, ...)
using namespace std;     // Benutze den Namensraum std

double f(double x, int wahl){                  // Deklaration und Definition der Funktion f(x) in Abhaengigkeit von der Funktionen-Auswahl
    double wert;                               // Deklaration des Rueckgabewertes der Funktion (y-Wert)
    
    switch( wahl ){                            // switch-Anweisung Anfang 
        case 1:                                // switch-Fall Nr.1
            wert = exp(x) - 10;                // Anweisung Funktion Nr.1
            break;                             // switch-Fall Nr.1 Ende
        case 2:                                // switch-Fall Nr.2
            wert = x*x - 10*x + 8;             // Anweisung Funktion Nr.2
            break;                             // switch-Fall Nr.2 Ende
        case 3:                                // switch-Fall Nr.3
            wert = pow(x,3) - 8*x*x + 2*x + 3; // Anweisung Funktion Nr.3
            break;                             // switch-Fall Nr.3 Ende
        case 4:                                // switch-Fall Nr.4
            wert = 10 * exp(-x/5) * sin(3*x);  // Anweisung Funktion Nr.4
            break;                             // switch-Fall Nr.4 Ende
        default:                               // switch-default
            wert = 0;                          // Anweisung default
            break;                             // switch-default Ende
    }                                          // switch-Anweisung Ende
    
    return wert;                               // Rueckgabewert der Funktion f(x)
}                                              // Ende der Funktion f(x)

int main(){                                                   // Hauptfunktion
    int auswahl;                                              // Deklaration der Integer Variable der Funktionen-Auswahl
    const double plot_a = 0;                                  // Untergrenze des x-Intervalls in dem die Ergebnisse ausgegeben werden sollen
    const double plot_b = 10;                                 // Obergrenze des x-Intervalls in dem die Ergebnisse ausgegeben werden sollen
    const int N = 200;                                        // Anzahl der Punkte in die das x-Intervall aufgeteilt wird
    const double dx = (plot_b - plot_a)/N;                    // Abstand dx zwischen den aequidistanten Punkten des x-Intervalls
    double x = plot_a - dx;                                   // Aktueller x-Wert
    
    cin  >> auswahl;                                          // Einlesen Funktionsnummer mittels der Tastatur

    printf("# 0: Index i \n# 1: x-Wert \n# 2: f(x)-Wert \n"); // Terminal Ausgabe
    
    for(unsigned i = 0; i <= N; ++i){                         // For-Schleife der Ausgabe der (x-y)-Wertetabelle
        x = x + dx;                                           // Aktueller x-Wert
        printf("%3d %20.10f %20.10f \n",i, x, f(x,auswahl));  // Ausgabe der (x-y)-Wertetabelle
    }                                                         // Ende for-Schleife 
}                                                             // Ende der Hauptfunktion

Die Terminalausgabe des C++ Programms wurde hierbei in eine Datei umgeleitet (' ./a.out > A4_2.dat ') und die Funktionswerte dann mittels des folgenden Python Skriptes dargestellt:

PythonPlot_A4_2.html
# Python Programm zum Plotten (x-y)-Wertetabelle (A4_2.cpp)
import matplotlib.pyplot as plt              # Python Bibliothek zum Plotten (siehe https://matplotlib.org/ )
import numpy as np                           # Python Bibliothek fuer Mathematisches (siehe https://numpy.org/ )

data = np.genfromtxt("./A4_2.dat")           # Einlesen der berechneten Daten von A4_2.cpp

plt.title(r'Plot der gewählten Funktion')    # Titel der Abbildung
plt.ylabel(r'$f(x)$')                        # Beschriftung y-Achse
plt.xlabel('x')                              # Beschriftung x-Achse
plt.plot(data[:,1],data[:,2], color="blue")  # Plotten der Funktion f(x)

plt.savefig("A4_2.png", bbox_inches="tight") # Speichern der Abbildung als Bild
plt.show()                                   # Zusaetzliches Darstellen der Abbildung im separaten Fenster
A4_2.sh
echo Bitte geben Sie die Funktionsnummer ein:
g++ A4_2.cpp
./a.out > A4_2.dat
python3 PythonPlot_A4_2.py

Die Ausführung des C++ und Python Programms wurden in einem Shell Skript (A4_2.sh, siehe nebenstehende Abbildung) zusammengefügt, wobei die Anweisung, was der Benutzer einzugeben hat, in dem Shell Skript mittels des Befehles echo gemacht wurde.



Musterlösung zur Aufgabe 3 (5 Punkte)

Die folgenden Ausdrücke wurden mittels eines C++ Programms berechnet. \[ \begin{equation} \prod_{k=1}^{11} k^2 \quad,\quad \sum_{k=0}^{20} \sum_{i=0\\i \neq k}^{10} \frac{k + i^3}{\left( k - i \right)^2} \quad,\quad \hbox{Bestimmen Sie N}\quad:\quad \sum_{k=0}^{N} \sum_{i=0\\i \neq k}^{250} \left( k + i^2 \right) = 672013120 \end{equation} \] Die Lösungen der ersten und dritten Aufgabe können hierbei nur ganzzahlige Werte annehmen, sodass lediglich die Lösung der zweiten Aufgabe auf 15 Stellen genau und die der ersten und dritten Aufgabe als Integerzahlen ausgegeben wurden.
Das C++ Programm sah wie folgt aus

A4_3.cpp
// Musterlösung der Aufgabe 3 des Übungsblattes Nr.4
#include <iostream>      // Ein- und Ausgabebibliothek
#include <cmath>         // Bibliothek für mathematisches (e-Funktion, Betrag, ...)

int main(){              // Hauptfunktion
    int k, i;            // Deklaration der Summenindexe 
    long A_1 = 1;        // Deklaration der double Variable für Aufgabe 2.1 und Initialisierung
    double A_2 = 0;      // Deklaration der double Variable für Aufgabe 2.2 und Initialisierung
    long A_3 = 0;        // Deklaration der double Variable für Aufgabe 2.3 und Initialisierung
    
    for(k=1; k<=11; ++k){                    // Schleifen Anfang
        A_1 = A_1 * k*k;                     // Produkt 1
    }                                        // Schleifen Ende

    for(k=0; k<=20; ++k){                                  // Schleifen Anfang der 1.Summe
        for(i=0; i<=10; ++i){                              // Schleifen Anfang der 2.Summe
            if( i != k ){                                  // if-Anweisung Anfang
                A_2  = A_2  + (k + pow(i,3))/pow(k - i,2); //Innerer, math. Teil der Doppelsumme
                }                                          // if-Anweisung Ende
        }                                                  // Schleifen Ende der 2.Summe
    }                                                      // Schleifen Ende der 1.Summe

    k=0;
    while( A_3 != 672013120  ){                            // Schleifen Anfang 1.Summe, mit Abbruchbedingung
        for(i=0; i<=250; ++i){                             // Schleifen Anfang der 2.Summe
            if( i != k ){                                  // if-Anweisung Anfang
                A_3  = A_3  + (k + i*i);                   //Innerer, math. Teil der Doppelsumme
                }                                          // if-Anweisung Ende
        }                                                  // Schleifen Ende der 2.Summe
        ++k;                                               // Erhöhung von k um eins wegen while-Schleife
    }                                                      // Schleifen Ende der 1.Summe

    // Terminal Ausgabe auf 15 Nachkommastellen
    printf("%-24s %-24s %-24s \n","Aufgabe 3.1","Aufgabe 3.2","Aufgabe 3.3, N=");
    printf("%-24li %-24.15f %-24i \n",A_1,A_2,k-1);
}

und produzierte die folgende Terminalausgabe: