Browse Category: Mathematik

Veranschaulichung der Ableitung einer Funktion

Viele Schüler und Studenten tun sich etwas schwer, wenn es darum geht sich die Ableitung einer Funktion vorzustellen. Sie können zwar häufig durch die Anwendung der Ableitungsregeln, die Funktion ableiten, oft ist es aber mehr als nur hilfreich, wenn man auch versteht, was sich hinter dieser elementaren Operation verbirgt. Im folgenden soll mit Hilfe von Mathematica vorgestellt werden, wie die Ableitung-Operation visualisiert werden kann.

Ableiten heisst die Steigung eines Punktes P auf einem Graphen G zu bestimmen. Man lernt auch, dass man sich diesen Vorgang durch Anlegen einer Tangente an diesen Punkt vorstellen kann.

Um das Vorstellungsproblem anschaulich zu machen, schaut man sich am einfachsten eine Funktion und die zugehörige Ableitung-Funktion an.In Mathematica geht das über folgende Anweisungen:

f[x_] := x^3 - 9 x + 5;
Plot[{f[x], f'[x]}, {x, -4, 4}]

Wir sehen in der Graphik in Blau die Ursprungsfunktion und in Orange die Ableitungsfunktion. Dabei wird nun schon sehr deutlich, dass es nicht gerade intuitiv möglich ist, sich die Steigung (oder die Tangente mit der entsprechenden Steigung) auf dem blauen Graphen vorzustellen.

Relativ einfach kann man sich mit Mathematica nun erstmal eine Punkt-Menge auf dem Graphen vorstellen, an denen wir zur Veranschaulichung dann auch die Tangente anlegen werden. Nun aber zuerst zur Punkt-Menge.

Als erstes benötigen wir eine Liste mit Koordinaten der Punkte die wir auf dem Graphen darstellen möchten. Dazu nutzen wir folgenden Funktionsaufruf, der eine Liste über die Table-Funktion von x und zugehörigen f(x) Werten erzeugt:

Punktliste = Table[{x, f[x]}, {x, -4, 4, 0.25}]
Das ergibt dann folgende Liste:
{{-4., -23.}, {-3.75, -13.9844}, {-3.5, -6.375}, {-3.25, -0.078125}, \
{-3., 5.}, {-2.75, 8.95313}, {-2.5, 11.875}, {-2.25, 13.8594}, {-2., 
  15.}, {-1.75, 15.3906}, {-1.5, 15.125}, {-1.25, 14.2969}, {-1., 
  13.}, {-0.75, 11.3281}, {-0.5, 9.375}, {-0.25, 7.23438}, {0., 
  5.}, {0.25, 2.76563}, {0.5, 
  0.625}, {0.75, -1.32813}, {1., -3.}, {1.25, -4.29688}, {1.5, \
-5.125}, {1.75, -5.39063}, {2., -5.}, {2.25, -3.85938}, {2.5, \
-1.875}, {2.75, 1.04688}, {3., 5.}, {3.25, 10.0781}, {3.5, 
  16.375}, {3.75, 23.9844}, {4., 33.}}

Mit der Epilog-Funktion können diese Koordinaten dann als Punkte auf dem Graphen dargestellt werden:

Plot[f[x], {x, -4, 4}, 
 Epilog -> {PointSize[0.015], Hue[1], Map[Point, Punktliste]}]

Nun stellt sich die Frage, wie man für alle diese Punkte eine Tangentengleichung bestimmt werden kann. Ausgangspunkt ist die bekannte Geradengleichung: y = m x + b. Wobei m die Steigung beschreibt und b den y-Achsenabschnitt also den Abstand zur x-Achse bezeichnet.

Allgemein gilt für die Tangente an einem bestimmten Punkt a eines Graphen der Funktion f(x) folgende Gleichung:

    \[y_t(x) = f(a) + f'(a) (x-a)\]

Mit Mathematica lässt sich das problem relativ einfach beschreiben. Man definiert die Ausgangsfunktion f(x) und die allgemeine Tangentengleichung t(x) und den Punkt a und lässt sich das Ganze dann über die Plot-Funktion anzeigen.

Möchte man dies nun für alle Punkte, mit einer kürzeren Tangente darstellen geht man wie folgt vor:

Für unsere Tangentengleichung kennen wir bereits die Steigung m, diese ergibt sich aus der Ableitung f'(x) an einer bestimmten Position x.

Der Parameter b (Y-Achsen-Abschnitt) bestimmen wir über den Funktionswert des Graphen.

Punktliste1 = Table[{x, f[x]}, {x, -4, 4, 0.75}];
Plot[f[x], {x, -4, 4}, 
 Epilog -> {PointSize[0.015], Hue[1], Map[Point, Punktliste1]}]

Die nun fehlende Ziel-Koordinate bestimmen wir über folgende Überlegung: Ausgangs-Koordinate ist der Punkt x_0 und der zugehörige Funktionswert f(x_0). Die Steigung des anzulegenden Pfeils entspricht der Ableitung f'(x_0) damit lässt sich nun die Ziel-Koordinate berechnen in dem man auf der x-Achse ein Stück h dazu addiert und y-Position über die Steigung und den y-Achsen-Abschnitt berechnet.

Tangentenliste = 
 Table[{{x, f[x]}, {(x + 0.25), f[x] + f'[x]*0.9}}, {x, -4, 4, 0.25}]

Mit diesen Informationen sind wir in der Lage die gewünschte Darstellung berechnen zu lassen:

Show[Plot[f[x], {x, -4, 4}, 
  Epilog -> {PointSize[0.015], Hue[1], Map[Point, Punktliste]}], 
 Graphics[Map[Arrow, Tangentenliste]], Plot[f'[x], {x, -4, 4}]]

Eine weitere Möglichkeit…mit Hilfe der Manipulate-Funktion

Manipulate bietet die Möglichkeit bestimmte Parameter zu verändern. Damit lassen sich insbesondere Zusammenhänge sehr einfach visualisieren. Im folgenden soll dies am Beispiel der Tangente sprich der Ableitung an der Sinus-Funktion vorgestellt werden:

f[x_] = Sin[x];
l[x_, a_] := 
 f[a] + f'[a] (x - a) /; 
  a - 1/(Sqrt[1 + (f'[a])^2]) <= x <= a + 1/(Sqrt[1 + (f'[a])^2])
Manipulate[
 Plot[{f[x], l[x, a]}, {x, 0, 2 \[Pi]}, PlotRange -> {-1.5, 1.5}], {a,
   0, 2 \[Pi]}]

Das ergibt dann in Abhängigkeit des Parameters a, der über die Manipulate-Funktion sowohl manuell als auch automatisch verändert werden kann, folgende Ausgabe:

Damit dürfte es möglich sein, die Ableitung einer Funktion in 2 Dimensionen jedem Interessierten näher zu bringen. Hat man das einmal verstanden ist der Weg in die dritte Demission und zu den partiellen Ableitungen nicht mehr weit.

Newton Iteration

Das nach Isaak Newton benannte Verfahren, ist in der Mathematik ein häufig verwendeter Approximations-Algorithmus zur numerischen Lösung von nichtlinearen Gleichungen.

Die grundlegende Idee dieses Verfahrens ist, die Funktion in einem Ausgangspunkt zu linearisieren, d. h. ihre Tangente zu bestimmen, und die Nullstelle der Tangente als verbesserte Näherung der Nullstelle der Funktion zu verwenden.

Die erhaltene Näherung dient dann als Ausgangspunkt für einen weiteren Verbesserungsschritt. Diese Iteration erfolgt solange, bis die Änderung in der Näherungslösung eine festgesetzte Schranke unterschritten hat.

Mathematica Essenzen

Seit ich als Student die ersten Berührungspunkte mit Mathematica bekommen habe, fasziniert mich der dadurch aufgespannte Möglichkeitsraum der funktionalen Programmierung. Ursprünglich als Software für symbolische Mathematik gedacht, ist es heute in der Version 12 ein Softwaresystem für alle denkbaren Anwendungsmöglichkeit, weit über die Mathematik oder Physik hinaus.

Deutsche Dokumentation der Version 2.0 aus dem Jahr 1992

Die Sprache ist immer noch die selbe wie 1988 in der Version 1 und wurde aber zwischenzeitlich ergänzt und überarbeitet und heisst nun Wolfram Language. Das Handbuch gibt es nur noch elektronisch, da es mehrere tausend Seiten umfasst.

Der Umgang ist leider etwas gewöhnungsbedürftig und ich lerne heute immer noch dazu. In den folgenden Abschnitten werde ich meine Einsichten die nicht in den gängigen Büchern stehen versuchen als eine Art lose Blatt Sammlung (Codex) zu dokumentieren.

Darstellung von Funktionen

Mathematica stellt umfassende Funktionen für die Darstellung von Funktionen zur Verfügung. Die zentrale Funktion ist die Plot[]-Funktion. Im nachfolgenden Bild, ist die Grundmenge Funktion und dann die zentralen Einstellungs-Möglichkeiten wie die Darstellung ergänzt werden kann dargestellt:

Elementare Plot-Funktion

Wie man sieht, wird zwar den groben Funktion Verlauf, wichtige Eigenschaften sind aber nicht ersichtlich. Dazu muss man die Darstellung-Bereiche in x- und y-Richtung anpassen und weitere Möglichkeiten einsetzen, um mehr über den Funktionsgraph zu erfahren.

Erweiterte Plot-Funktionen

Mathematica stellt eine Vielzahl von Darstellung-Optionen bereit, die alle in der Online-Dokumentation aufgeführt sind.

Kurvendiskussion

Eine sehr häufig vorkommende Aufgabe ist die Untersuchung einer gegeben Funktion hinsichtlich Ihrer Eigenschaften wie Nullstellen, Extrema, Asymptoten etc. Wie das mit Hilfe von Mathematica gemacht werden kann, wird im folgenden vorgestellt.

Ausgangspunkt sei folgende Funktion und ihr Graph wie im nachfolgenden Bild dargestellt.

Bestimmung der Nullstellen

Um die Nullstellen zu bestimmen, sind die Punkte zu berechnen, an denen der Funktionswert von f(x) = 0 ist. Dazu gibt es zwei vordefinierte Funktionen in Mathematica: Solve und NSolve. NSolve versucht die Funktion mit hilfe numerischer Methoden zu lösen wo hingegen Solve versucht die Funktion aufzulösen, was natürlich mehr Rechenzeit erfordern kann und bisweilen auch nicht möglich ist.

NSolve[f[x] == 0]   ==>  {{x -> -3.24655}, {x -> 0.576888}, {x -> 2.66966}}

Die beiden Funktionen bestimmen sämtliche realen und komplexen Nullstellen eines Polynoms und geben das Ergebnis in Form einer Liste wieder. Also nicht in Form von Variablen denen die Werte bereits zugewiesen wurden. D.h. wir können auf diese Lösungen noch nicht ohne weitere Schritte zugreifen.

Bestimmung der 1. und 2. Ableitungen

Mathematica erlaubt es Ableitungen in Analogie zur händischen Form einfach durch Anfügen eines Ableitungsstrichs zu erzeugen. Formal richtig wäre aber die Benutzung der Funktion D[f[x], x].

f'[x]  ==> -9 + 3 x^2
f''[x] ==> 6x

Mit diesen Vorarbeiten lässt sich die komplette Kurvendiskussion, d.h. die Bestimmung der Nullstelle und der Extremwerte mit wenigen Zeilen berechnen:

Lösungen = {x, f[x]} /. NSolve[f[x] == 0]
{{-3.24655,0.}, {0.576888, -8.88178*10^-16}, {2.66966, -3.55271*10^-15}}

Extrema = {x, f[x]} /. NSolve[f'[x] == 0]
{{-1.73205, 15.3923}, {1.73205, -5.3923}}

Plot[f[x], {x, -4, 4}, 
 Epilog -> {{PointSize[0.02], Red, Map[Point, Lösungen],
    PointSize[0.02], Magenta, Map[Point, Extrema]}}]

Anschliessend sorgt die Plot-Funktion für die visuelle Darstellung:

Gleitende Mittelwerte

Arithmetischer Mittelwert

Der Mittelwert beschreibt den statistischen Durchschnittswert. Für die Ermittlung des Mittelwertes addiert man alle Werte einer Datenmenge und teilt die Summe durch die Anzahl aller Werte.

RandomInteger[{1, 10}, 20] = {9, 8, 8, 7, 1, 7, 2, 4, 1, 5, 9, 3, 5, 4, 4, 5, 8, 10, 3, 9}

Mean[{9, 8, 8, 7, 1, 7, 2, 4, 1, 5, 9, 3, 5, 4, 4, 5, 8, 10, 3, 9}] ==> 28/5 = 5.6

Median

Der Median oder Zentralwert ist der Wert, der genau in der Mitte einer Datenmenge liegt. Die eine Hälfte aller ist immer kleiner, die andere größer als der Median. Bei einer geraden Anzahl von Individualdaten ist der Median die Hälfte der Summe der beiden in der Mitte liegenden Werte.

Median[{1, 2, 3, 4, 5, 6, 7}] ==> 4
oder
Median[{1, 2, 3, 4, 5, 6, 7, 8}] ==> 9/2
oder
Median[{9, 8, 8, 7, 1, 7, 2, 4, 1, 5, 9, 3, 5, 4, 4, 5, 8, 10, 3, 9}] ==> 5

Gleitender Mittelwert

Gleitende Mittelwerte dienen zur Glättung eines gegebenen Datenverlaufes. Die Glättung erfolgt durch das Abschwächen von besonders hohen oder niedrigen Werten. Die geschieht, indem eine neue Datenreihe erstellt wird, die aus den Durchschnitten von gleich großen Datenmengen berechnet werden. Dabei werden die Datenpunkte der Reihe durch den arithmetischen Mittelwert der Nachbarpunkte (oder einer gewichteten Form davon) ersetzt. Im einfachsten Fall geschieht dies durch Mittelung von drei Datenpunkten (der ausgewählte Datenpunkt und seine beiden Nachbarn):

MovingAverage[{a, b, c, d, e, f, g}, 3]
{1/3 (a + b + c), 1/3 (b + c + d), 1/3 (c + d + e), 1/3 (d + e + f), 
 1/3 (e + f + g)}

Beispiel

Ein Unternehmen liefert nachfolgende dargestellte Menge von Umsatzzahlen. Wir berechnen nun den gleitenden Mittelwert 3- Ordnung:

Wie oben beschrieben, nimmt man die n-Datenpunkte im Beispiel ist n=3, berechnet den Mittelwert und ersetzt das erste Datenelement durch diesen Mittelwert. Dann rückt man ein Feld weiter, berechnet wieder den Mittelwert, das ergibt dann den zweiten Eintrag usw.

Damit werden Datenreihen geglättet. Der gleitende Mittelwert wirkt wie ein Dämpfungsfilder, der die größten Ausreisser in der Zahlenreihe glättet.

Wie man das in der Praxis anwenden kann ist sehr anschaulich auf den Seiten von Matthias Busse vorgestellt. Er zeigt wie man einen Datenstrom (simuliert durch einen Strom von Zufallszahlen) glätten kann.

// Über viele Integer Werte einen Mittelwert bilden.
// Nach Matthias Busse Version 1.0

#define anzahlMittelWerte 10
int werte[anzahlMittelWerte]
int zaehlerMittelWerte=0;

void setup() {
  Serial.begin(9600);
}

void loop() {
  int n;
  float f;
  n=random(1,10);
  f= mittelWert(n);
  Serial.println(f);
  delay(100);
}

float mittelWert(int neuerWert) {// neuen Datenwert aufnehmen und Mittelwert zurück geben
   float mittel, summe=0;
   werte[zaehlerMittelWerte] = neuerWert;
   for(int k=0; k < anzahlMittelWerte; k++) 
      summe += werte[k];

  mittel=(float) summe / anzahlMittelWerte; 
  zaehlerMittelWerte++;

  if(zaehlerMittelWerte >= anzahlMittelWerte) 
      zaehlerMittelWerte=0;
  return mittel;
}

Eine etwas andere Lösung findet sich im Arduino Forum:

/*
  Smoothing
  http://www.arduino.cc/en/Tutorial/Smoothing
*/

// Define the number of samples to keep track of. The higher the number, the
// more the readings will be smoothed, but the slower the output will respond to
// the input. Using a constant rather than a normal variable lets us use this
// value to determine the size of the readings array.
const int numReadings = 10;

int readings[numReadings];      // the readings from the analog input
int readIndex = 0;              // the index of the current reading
int total = 0;                  // the running total
int average = 0;                // the average

int inputPin = A0;

void setup() {
  // initialize serial communication with computer:
  Serial.begin(9600);
  // initialize all the readings to 0:
  for (int thisReading = 0; thisReading < numReadings; thisReading++) {
    readings[thisReading] = 0;
  }
}

void loop() {
  // subtract the last reading:
  total = total - readings[readIndex];
  // read from the sensor:
  readings[readIndex] = random(1,10); ;
  // add the reading to the total:
  total = total + readings[readIndex];
  // advance to the next position in the array:
  readIndex = readIndex + 1;

  // if we're at the end of the array...
  if (readIndex >= numReadings) {
    // ...wrap around to the beginning:
    readIndex = 0;
  }

  // calculate the average:
  average = total / numReadings;
  // send it to the computer as ASCII digits
  Serial.println(average);
  delay(100);        // delay in between reads for stability
}

Das Josephus Problem

Der Legende von Josephus nach, trieben die Römer eine Gruppe von Kriegern in eine Höhle, um diese dann gefangen zu nehmen. Nach der Legende entschieden diese Krieger aber dann sich nicht gefangen nehmen zu lassen, sondern lieber den Heldentod zu sterben d.h. sich gegenseitig umzubringen. In dem jeder seinen nächsten in der Reihe töten sollte. Also der Erste tötet den Zweiten, der Dritte den Vierten usw.
Nun stellt sich natürlich die Frage, sind alle Positionen gleich gut oder gibt es eine günstigste Position?

Um diese Frage zu beleuchten, formulieren wir das Problem etwas weniger kriegerisch um: Dazu stellen uns eine Menge n mit Personen vor, die in einer Reihe stehen und definieren zwei Funktionen:
RotateLeft: Nimmt die erste Person in der Reihe und stellt diese ans Ende der Reihe
Rest: Entfernt die nächste Person in der Reihe

Der Ablauf sieht nun wie folgt aus: Wir starten mit dem Aufruf der Funktion RotateLeft, diese nimmt die erste Person nach dem diese seinen Nachfolger virtuell erschlagen hat von der Liste und setzt sie am Ende ein. Dann folgt der Aufruf der Funktion Rest, diese eliminiert die zweite Person aus der Liste. Dann folgt wieder die Funktion RotateLeft, gefolgt von Rest usw. Dieser Prozess läuft solange bis nur noch eine Person übrig ist.

Mit Hilfe von Mathematica lässt sich dieser Ablauf sehr anschaulich programmieren und nachvollziehbar darstellen. Wir schreiben dazu eine rekursive Funktion mit dem Namen survivor[liste]. Dieser Funktion übergeben wir immer wieder die aktualisierte Liste der Personen.

Survivor[liste_] := 
 Nest[(Rest[RotateLeft[#]]) &, liste, Length[liste] - 1]

Mit Hilfe der Trace-Funktion die wir auf die RotateLeft Funktion anwenden, wird der rekursive Programm-Ablauf deutlicher:

TracePrint[Survivor[Range[20]], RotateLeft]

    RotateLeft
   {2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,1}
    RotateLeft
   {4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,1,3}
    RotateLeft
   {6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,1,3,5}
    RotateLeft
   {8,9,10,11,12,13,14,15,16,17,18,19,20,1,3,5,7}
    RotateLeft
   {10,11,12,13,14,15,16,17,18,19,20,1,3,5,7,9}
    RotateLeft
   {12,13,14,15,16,17,18,19,20,1,3,5,7,9,11}
    RotateLeft
   {14,15,16,17,18,19,20,1,3,5,7,9,11,13}
    RotateLeft
   {16,17,18,19,20,1,3,5,7,9,11,13,15}
    RotateLeft
   {18,19,20,1,3,5,7,9,11,13,15,17}
    RotateLeft
   {20,1,3,5,7,9,11,13,15,17,19}
    RotateLeft
   {3,5,7,9,11,13,15,17,19,1}
    RotateLeft
   {7,9,11,13,15,17,19,1,5}
    RotateLeft
   {11,13,15,17,19,1,5,9}
    RotateLeft
   {15,17,19,1,5,9,13}
    RotateLeft
   {19,1,5,9,13,17}
    RotateLeft
   {5,9,13,17,1}
    RotateLeft
   {13,17,1,9}
    RotateLeft
   {1,9,17}
    RotateLeft
   {17,9}
  RotateLeft
   {9}

An diesem Beispiel wird deutlich, was für ein mächtiges Werkzeug Mathematica ist, um sehr komplexe Fragestellungen zu untersuchen.