Darstellung
Eingeführt wurden Algorithmen als Methode, Abläufe zu beschreiben. Aber wie können einzelne Schritte, Rechnungen, Instruktionen, Verfahren etc. ausgedrückt werden? Einem anderen Menschen einen Algorithmus zu erklären, ist sprachlich nicht schwierig, doch heutzutage soll ein Algorithmus meist von einem Computer (oder ähnlichen Maschinen) ausgeführt werden und dieser spricht eine eigene Sprache.
Allerdings ist beim Erlernen von Algorithmen das Verständnis der Struktur das Ziel und nicht das Auswendiglernen konkreter Umsetzungen. Deswegen wird hier nur kurz auf die konkrete Computer-Mensch-Kommunikation eingegangen und anschließend eine Methode zur systematischen Notation von Algorithmen eingeführt.
Programmiersprachen
Die Sprache eines Computers ist der Maschinencode, der nur aus Einsen und Nullen besteht. Um einen Algorithmus für Computer verständlich zu machen, müssen daher eigentlich Programmierer diese Maschinensprache beherrschen. Das Problem ist jedoch, dass diese für nahezu jeden Menschen zu kompliziert bzw. aufwendig ist. Aus diesem Grund werden höhere Programmiersprachen genutzt, wie z.B. Basic, C/C++, Java, Python, Ruby oder andere. Diese Sprachen sind für Menschen verständlicher, sodass sie einfacher gelernt werden können und bilden damit eine Kommunikationsmöglichkeit zwischen Mensch und Maschine. Nachdem es sich bei den verschiedenen Programmiersprachen nicht um die eigentliche Sprache des Computers handelt, werden die Instruktionen durch einen sogenannten Compiler in den Maschinencode übersetzt. Den Vorgang, eine Idee in Programmiersprache zu übersetzen, nennt man Implementierung.
Pseudocode
Das Erlernen einer einzelnen Programmiersprache ist jedoch immer noch sehr zeitintensiv – hauptsächlich wegen der speziellen Syntax und der Tatsache, dass meist zeitgleich eine Einführung in höhere Softwaredesignprinzipien wie eben Algorithmen erfolgt. Das kann vor allem im Schulkontext für Schülerinnen und Schüler überfordernd sein. Allerdings gibt es eine Alternative zum Erlernen einer kompletten Programmiersprache, die sich für den Schulgebrauch, besonders in der Sekundarstufe, anbietet: Pseudocode.
Ein Pseudocode ist eine präzise Formulierung eines Algorithmus, die hauptsächlich aus umgangssprachlichen Formulierungen besteht, aber auch Schlüsselwörtern, die in allen Programmiersprachen in ähnlicher Weise auftauchen. Zudem besteht der Pseudocode aus mathematischen Formeln. Zu beachten ist, dass ein Pseudocodeplan nicht unbedingt eins zu eins in den Programmcode überführt werden kann, da gewisse Formulierungen, Funktionen und Operationen in der gewünschten Programmiersprache eventuell nicht direkt zur Verfügung stehen. Der große Vorteil ist allerdings, dass man durch die Umgangssprache die grundlegende Idee eines Algorithmus skizzieren und vermitteln kann. Damit wird der Fokus auf die Problemlösung selbst gesetzt. Deswegen hat sich Pseudocode in der Softwareentwicklung verbreitet, da er es Wissenschaftlern und Programmierern aus verschiedenen Bereichen/Branchen erlaubt, über dasselbe zu sprechen. Für den Schulunterricht ist der Vorteil, dass dadurch über die Algorithmen an sich diskutiert werden kann und die Lernenden nicht durch Schwierigkeiten bei der Implementierung behindert werden. Im Folgenden wird die Darstellung verschiedener Algorithmen via Pseudocode anhand einiger Beispiele veranschaulicht.
Am Anfang des letzten Abschnitts zur Begriffsbildung wurde über das Kochen von Nudeln als Algorithmus gesprochen. Kochrezepte sind, im Normalfall, schon direkt als Pseudocode geschrieben: Sie sind formal korrekt und verwenden Umgangssprache, aber auch Fachbegriffe der konkreten Umsetzung und ein paar mathematische Begriffe. Konkret lautet der Pseudocode für z. B. das Kochen von Nudeln wie folgt:
function nudeln_Kochen() {
Einen Topf auf den Herd stellen
Nudeln wiegen
Zehnmal soviel Wasser wie Nudeln in den Top füllen
Das Wasser aufkochen lassen
Salz hinzugeben
Umrühren
Nudeln hinzugeben
Solange Nudeln noch nicht bissfest sind
| Einmal pro Minute umrühren
Nudeln abgießen
}
Da Rezepte und Kochanweisung aber nicht mittels einer Programmiersprache umgesetzt und anschließend in Maschinencode übersetzt werden müssen, unterscheidet sich diese Darstellung kaum von einer rein umgangssprachlichen. Das Einzige, was hier an ein echtes Computerprogramm erinnert, ist,
- dass der gesamte Ablauf in geschweifte Klammern gefasst ist und
- dass das Ganze als function deklariert wurde,
um zu zeigen, dass alle Schritte zusammengehören, und einen geschlossenen Handlungsablauf darstellen. Dies wird im nächsten Abschnitt noch genauer erklärt.
Dieses Beispiel bietet sich gut für den Schulunterricht an, wie die nachfolgende Szene 2 - Teil 1: Eigenschaften und Darstellung von Algorithmen zeigt, da den Schülerinnen und Schülern solche Handlungsabläufe aus dem Alltag bekannt sind.
Ein stärkerer Kontrast zu realen Abläufen entsteht, wenn eine Aufgabe aus dem Mathematikunterricht betrachtet wird: Gegeben sei eine Polynomfunktion zweiten Grades $f(x) = a \cdot x^2 + b \cdot x + c$ und gesucht sei die Anzahl und Koordinaten der Nullstellen dieser Funktion. Die Antwort eines Lernenden könnte dann zum Beispiel wie folgt aussehen:
“Man verwendet erst die Mitternachtsformel
\[x_{1,2}=\frac{-b\pm\sqrt{b^2 - 4ac}}{2a}\]
und weiß somit die Nullstellen. Dann schaut man, ob $b^2 - 4ac$ größer oder kleiner Null ist und weiß, wie viele es sind.”
Je nach Klassenstufe und Kontext – im Unterricht, in einer Schulaufgabe etc. – ist so eine Antwort hinreichend gut, um zu sehen, dass die Schülerinnen und Schüler das Verfahren verstanden haben. Soll dieses allerdings sauber als Ablaufplan/Rezept aufgeschrieben werden, bietet es sich wieder an Pseudocode zu verwenden:
function nullstellen_bestimmen(a, b, c) {
Berechnung der Diskriminante d = b^2 - 4*a*c
Ist d < 0
| Ausgabe der Antwort "Es gibt keine Nullstellen."
Ist d == 0
| Ausgabe der Antwort "Es gibt genau eine Nullstelle bei -b/(2*a)."
Ist d > 0
| Ausgabe der Antwort "Es gibt zwei Nullstellen.
| Eine bei (-b + sqrt(d))/(2*a) und
| eine bei (-b - sqrt(d))/(2*a)."
}
Hier deuten die übergebenen Parameter a, b, c
in der Funktionsdefinition an, dass das Problem alleine von den Koeffizienten des Polynoms abhängt und nicht etwa von der Reihenfolge der Summanden oder davon, dass die Unbestimmte des Polynoms $x$ lautet. Außerdem werden bereits einige Schreibweisen verwendet, die in vielen Programmiersprachen üblich sind. In etwa die Verwendung von * zur Darstellung der Multiplikation oder der Operator == zur Überprüfung, ob zwei Werte gleich sind.
Abschließend soll noch der Unterschied zwischen Pseudocode und konkreten Implementierungen in bestimmten Programmiersprachen hervorgehoben werden. Dazu ein Beispiel, dass im Laufe dieses Moduls noch öfters zu sehen sein wird: Die Berechnung der Fakultät einer natürlichen Zahl. Der Wert $n!$ entsteht dadurch, dass alle Zahlen von $1$ bis $n$ aufmultipliziert werden, wobei $0!=1$ gesetzt wird. Der zugehörige Pseudocode, der diesen Wert berechnet, lautet wie folgt:
function fakultaet(n) {
Ist n == 0? Falls ja,
| Gib das Ergebnis 1 aus
Falls nein,
| Nimm alle Zahlen von 1 bis n und multipliziere sie sukzessive zusammen
}
Eine konkrete Implementierung in z. B. der Programmiersprache C/C++ sieht wie folgt aus:
public int fakultaet(int n) {
if(n == 0)
return 1;
else
result = 1;
for(i = 1; i <= n; i++) {
result *= i;
}
return result;
}
Sie unterscheidet sich in ihrer Struktur nicht vom Pseudocode – nur in einzelnen, konkreten Begriffen und Schreibweisen. Eine Implementierung in der Programmiersprache Ruby hingegen kann wie folgt aussehen:
def fakultaet(n)
n.zero? ? 1 : (1...n).inject(:*)
end
Von grundlegenden Unterschieden wie der Verwendung von def…end abgesehen besitzt die Sprache Ruby die spezielle Funktion inject, welche den Code verkürzt. Nach einem Blick auf die konkrete Implementierung dieser Funktion wird klar, dass sie auch nichts Anderes macht, als die Zahlen von $1$ bis $n$ aufzumultiplizieren. Der Algorithmus an sich bleibt also unverändert. Allerdings erlaubt es die Eigenheit dieser Programmiersprache, den Code zu verkürzen. Zudem wurde hier der Ternäroperator verwendet: Dieser ist von der Form
(Bedingung) ? (Code 1) : (Code 2)
und ist nichts weiter als eine abkürzende Schreibweise für die Fallunterscheidung
Ist (Bedingung) wahr? Falls ja,
führe (Code 1) aus.
Falls nein,
führe (Code 2) aus.
Der Hauptzweck des Pseudocodes ist es, solche Feinheiten der Implementierung zu umgehen, damit das Augenmerk auf den Algorithmen an sich bleiben kann.
Mehr Informationen zu Algorithmen finden Sie unter (Dieker & Güting, 2018).