Browse Category: Mathematik

Pure Funktionen oder Lambda-Ausdrücke

Reine oder Pure Funktionen sind auch bekannt als anonyme Funktionen oder auch Lambda-Ausdrücke. Reine Funktionen zeichnen sich insbesondere dadurch aus, dass sie immer das gleiche Ergebnis zurückgeben, wenn sie mit den gleichen Argumenten aufgerufen werden. Dank dieser Eigenschaft, referenzielle Transparenz genannt, ist es für derartige Funktionen nicht möglich, Seiteneffekte zu besitzen. Eine reine Funktion ist also ein rechnerisches Analogon einer mathematischen Funktion.

Eine Pure Function ist demnach eine ganz normale Programmfunktion, die lediglich zwei besondere Eigenschaft hat:

  1. Für die gleichen Eingabeparameter wird stets der gleiche Rückgabewert geliefert
  2. Es existieren keine Side-Effects (Nebenwirkungen)

Durch diese beiden Merkmale ist eine Pure Function vergleichbar mit einer schlichten mathematischen Funktion y = f(x), die für denselben x-Wert immer denselben y-Wert ermittelt.

Das bedeutet in der Praxis, dass die gesamte Programmlogik einer Funktion allein mit den Daten aus den Eingabeparametern arbeitet und als Ergebnis genau ein Rückgabewert entsteht. Während des Funktionsaufrufes entstehen keinerlei Side-Effects, wie beispielsweise das Verändern des Zustandes von Variablen außerhalb der Funktion oder der Eingabeparameter, das Schreiben in die Datenbank bzw. einer Datei oder der Aufruf anderer Funktionen, die selbst Side-Effects besitzen.

Das Konzept der Pure Function lässt sich nicht nur relativ leicht erklären, sondern ermöglicht in der Praxis Software zu schreiben, die vom Entwickler einfach zu verstehen ist, da die einzelnen Funktionen untereinander keine Abhängigkeiten besitzen und für sich allein geschrieben, verstanden und getestet werden können. Dies ist ein riesiger Vorteil. 

Ein Großteil der heute geschriebenen Software folgt jedoch einem anderen Paradigma, dem objektorientierten Modell (OO-Modell), welches genau das Gegenteil propagiert. In der objektorientierten Welt werden Funktionen als Methoden bezeichnet und zusammen mit den dazugehörigen Daten von Objekten (Klassen) gekapselt. Beim Aufruf einer Methode kann diese sowohl auf die übergebenen Eingabeparameter als auch auf die Daten des Objektes selbst zugreifen. Dabei wird bewusst der Zustand eines Objektes über die entsprechenden Methoden manipuliert, es werden also die Daten im Objekt selbst verändert. Zusätzlich werden die Daten eines Objektes als Rückgabewert eines Methodenaufrufes an andere Objekte weitergereicht und unterliegen dort der weiteren Manipulation.

Beispiel von Pure Funktion in der Wolfram Language

Es gibt mehrere gleichwertige Möglichkeiten, reine Funktionen in der Wolfram Language zu schreiben. Die Idee besteht in allen Fällen darin, ein Objekt zu konstruieren, das mit entsprechenden Argumenten eine bestimmte Funktion berechnet. Wenn also beispielsweise fun eine reine Funktion ist, dann wertet fun[a] die Funktion mit dem Argument a aus.

Die Wolfram Language erlaubt die Verwendung sogenannter reiner Funktionen (pure functions). Ihr erstes Argument wird gekennzeichnet durch das “#” Symbol und deren Ende ist mit dem “&”-Symbol gekennzeichnet.

So wie der Name einer Funktion irrelevant ist, wenn Sie nicht erneut auf die Funktion verweisen möchten, sind auch die Namen von Argumenten in einer reinen Funktion irrelevant. Die Wolfram Language ermöglicht es Ihnen, explizite Namen für die Argumente reiner Funktionen zu vermeiden und stattdessen die Argumente durch die Angabe von “Slot-Nummern” #n zu spezifizieren. In einer reinen Funktion von Wolfram Language steht #n für das n-te Argument, das man angeben kann. # steht für das erste Argument. . #2 steht für das zweite Argument usw.

Das nachfolgende Beispiel soll dieses Vorgehen veranschaulichen. Es soll eine Funktion definiert werden, die immer 1 addiert:

(#+1)& 

Wenn diese Funktion nun als Kopf eines Ausdrucks angegeben wird, so wird die Funktion auf alle Argumente angewendet:

(#+1)&[50]  --->  51
(#+1)&[{50,60}] ---> 51,61

Das geht natürlich auch mit mehreren Argumenten, wie das nachfolgende Beispiel zeigt:

{#2, 1 + #1, #1 + #2} &[a, b]  ---> {b, 1+a, a+b}

Grundsätzlich lassen sich beliebig viele Argumente definieren. Ab einer bestimmten Anzahl wird es aber doch sehr herausfordernd, noch die Übersicht zu behalten. Reine Funktionen in der Wolfram Language können eine beliebige Anzahl von Argumenten annehmen. Sie können ## verwenden, um alle angegebenen Argumente anzusprechen, und ##n für das n-te und nachfolgende Argumente.

f[##, ##] &[x, y]  ---> f[x,y,x,y]

Reine Funktionen ermöglichen es, Funktionen anzugeben, die auf Argumente angewendet werden können, ohne explizite Namen für die Funktionen definieren zu müssen.

Weitere Beispiele…

#^2& is a short form for a pure function that squares its argument:

Map[#^2 &, a + b + c]


This applies a function that takes the first two elements from each list. By using a pure function, you avoid having to define the function separately:

Map[Take[#, 2] &, {{2, 1, 7}, {4, 1, 5}, {3, 1, 2}}]

Berechnung des Oster-Datums

Methode nach C.F. Gauß

Die Berechnung des Oster-Datums erfolgt nach Regeln, die im Jahre 325 auf dem Konzil von Nicäa beschlossen wurden. Dem zu Folge das Osterfest am ersten Sonntag nach dem Frühlingsvollmond gefeiert werden soll. Fällt aber der erste Frühlingsvollmond auf einen Sonntag, so solle das Osterfest eine Woche später erfolgen. Da der Frühlingsvollmond im gregorianischen Kalender frühestens am 21. März und spätestens am 18. April eines Jahres liegen kann, schwankt der Ostertermin demnach immer zwischen dem 22. März und dem 25. April.

Der Algorithmus von C.F. Gauß besteht aus einer Abfolge von Rechenschritten die im folgenden dargestellt sind:

Ausgangspunkt ist immer eine Jahreszahl “Jahr” für die das Oster-Datum berechnet werden soll.

Erster Schritt: Bestimmung der erforderlichen Hilfszahlen

M und N seien zwei Hilfszahlen, die sich aus folgendem Algorithmus berechnen:

M = Rest der Division von (15 - p + k - q) : 30 
N = Rest der Divison von (4 + k - q) : 7 
k = Jahreszahl ohne die letzten beiden Ziffern  
p = Ganzzahliger Anteil der Division (13 + 8k) : 25
q = Ganzzahliger Anteil der Division k : 4


Im Gregorianischen Kalender liegen die Werte für die Jahre 1900 bis 2099 immer bei M= 24 und N = 5. Über die obige Rechenformel lassen sich aber auch Werte die vor 19oo liegen bzw. solche die ab 2100 dann Gültigkeit besitzen.

Zweiter Schritt: Berechnung der weiteren Werte

a = Rest der Division Jahr : 19
b = Rest der Division Jahr : 4
c = Rest der Division Jahr : 7
d = Rest der Division (19a + M) : 30
e = Rest der Division (2b + 4c + 6d + N) : 7

Berechnung des Oster-Datums

Ostern ist dann der

Ostern = (22 + d + e)te März, sofern der Wert zwischen 1 und 31 liegt, 
sonst ist 
Ostern = (d + e -9)te April

Es gibt aber wie oben beschrieben einige Randbedingungen zusätzlich zu berücksichtigen:

Ist d = 29 und e = 6,            dann ist Ostern nicht am 26. April sondern am 19. April
Ist d = 28 und e = 6 und a > 10, dann ist Ostern nicht am 25. April sondern am 18. April

Programmtechnische Umsetzung

Damit haben wir alle Schritte zusammen, die sich C.F. Gauß überlegt hat und können nun mit Hilfe beliebiger Programmiersprachen ein Programm erstellen, mit dessen Hilfe das Oster-Datem bestimmt werden kann. In Mathematica (Wolfram Language) könnte das wie folgt berechnet werden:

M=24; Nu=5;

For[Jahr = 1950, Jahr < 1980, Jahr++, a = Mod[Jahr, 19];
 b = Mod[Jahr, 4];
 c = Mod[Jahr, 7];
 d = Mod[(19*a + M), 30];
 e = Mod[(2 b + 4 c + 6 d + Nu), 7];
 
 Ostern1 = (22 + d + e);
 Ostern2 = (d + e - 9);
 
 If[(d == 29 && e == 6), Ostern2 = 19, Ostern2 = (d + e - 9)];
 If[(d == 28 && e == 6 && a > 10), Ostern2 = 18, 
  Ostern2 = (d + e - 9)];
 
 If[(Ostern1 < 1 || Ostern1 <= 31), {Print[Jahr, " Ostern ist am ", 
    Ostern1, ". März"]; Print[]}, Print["Ostern liegt im April"]];
 
 If[(Ostern2 >= 1 ), {Print[Jahr, " Ostern ist am ", Ostern2, 
    ". April"]; Print[]}, {Print["Ostern liegt im März s.o."], 
   Print[]}]]

Das könnte man natürlich noch viel eleganter ausführen, aber für einen ersten Test des Algorithmus müsste die Umsetzung genügen:

1950 Ostern ist am 9. April

1951 Ostern ist am 25. März

1952 Ostern ist am 13. April

1953 Ostern ist am 5. April
 
1954 Ostern ist am 18. April
 
:
:
:

1976 Ostern ist am 18. April

1977 Ostern ist am 10. April

1978 Ostern ist am 26. März
 
1979 Ostern ist am 15. April

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.