In diesem Dossier wiederholen wir die Grundwerkzeuge des Programmierens. Wir beschränken uns bewusst auf sehr wenige Sprachelemente: Variablen, Ein-/Ausgabe, Verzweigungen (if/elif/else) und while-Schleifen. Alles andere (for-Schleifen, Funktionen, Listen, Dictionaries) ist nicht erlaubt – ausser in den Zusatzaufgaben ganz am Schluss.
Das mag zuerst nach einer Einschränkung aussehen. Tatsächlich ist es aber genau umgekehrt: Mit diesen wenigen Bausteinen lässt sich jedes Programm schreiben. Wer sie sicher beherrscht, hat das Fundament gelegt.
Die Aufgaben werden von Kapitel zu Kapitel anspruchsvoller. Löse sie der Reihe nach und schaue dir die Lösung erst an, wenn du selbst einen ernsthaften Versuch unternommen hast.
Wichtige Python-Befehle
# Ausgabe
print("Hallo Welt")
print("Die Summe ist", summe) # mehrere Dinge mit Komma trennen
print("Die Summe ist " + str(summe)) # oder als String zusammenhängen
print(f"Die Summe ist {summe}") # f-String (elegant)
print("kein Zeilenumbruch", end=" ") # Zeilenumbruch unterdrücken
# Eingabe (input() liefert IMMER einen String!)
name = input("Wie heisst du? ")
zahl = int(input("Gib eine ganze Zahl ein: "))
kommazahl = float(input("Gib eine Kommazahl ein: "))
# Rechenoperatoren
7 + 3 # 10 Addition
7 - 3 # 4 Subtraktion
7 * 3 # 21 Multiplikation
7 / 3 # 2.333... Division (ergibt immer einen float!)
7 // 3 # 2 Ganzzahldivision (schneidet den Rest ab)
7 % 3 # 1 Modulo: der Rest bei der Division
7 ** 3 # 343 Potenz
# Vergleichsoperatoren (ergeben True oder False)
a == b # gleich (ACHTUNG: zwei Gleichheitszeichen!)
a != b # ungleich
a < b # kleiner
a <= b # kleiner oder gleich
a > b # grösser
a >= b # grösser oder gleich
# Logische Verknüpfungen
a > 0 and b > 0 # beide Bedingungen müssen erfüllt sein
a > 0 or b > 0 # mindestens eine Bedingung muss erfüllt sein
not (a > 0) # Umkehrung
# Verzweigung
if temperatur > 25:
print("heiss")
elif temperatur > 15:
print("angenehm")
else:
print("kalt")
# while-Schleife
i = 1
while i <= 10:
print(i)
i = i + 1
# Zufallszahlen
import random
random.randint(1, 100) # ganze Zufallszahl zwischen 1 und 100 (beide inklusive)
# Nützliches
abs(-3.5) # 3.5 Betrag
round(3.14159, 2) # 3.14 runden
"*" * 5 # "*****" String vervielfachen
Der Werkzeugkasten: 6 Muster, die du auswendig können musst
Fast jede Aufgabe in diesem Dossier ist eine Kombination der folgenden Muster. Präge sie dir gut ein!
1. Der Zähler – zählt, wie oft etwas passiert ist:
anzahl = 0 # Startwert: noch nichts gezählt
...
anzahl = anzahl + 1 # ein Treffer mehr
2. Der Summierer (Akkumulator) – addiert Werte auf:
summe = 0 # Startwert: 0, denn 0 ändert bei der Addition nichts
...
summe = summe + zahl
3. Der Multiplizierer – multipliziert Werte auf:
produkt = 1 # Startwert: 1, denn 1 ändert bei der Multiplikation nichts
...
produkt = produkt * zahl
4. Die Laufvariable – zählt die Schleifendurchgänge:
i = 1
while i <= n:
...
i = i + 1 # Vergisst man diese Zeile -> Endlosschleife!
5. Das Maximum (bzw. Minimum) – merkt sich den bisher besten Wert:
maximum = erste_zahl # der erste Wert ist der bisher beste
...
if zahl > maximum:
maximum = zahl
6. Das Flag (die Merk-Variable) – merkt sich, ob etwas eingetreten ist:
gefunden = False # bis jetzt haben wir nichts gefunden
...
if bedingung:
gefunden = True
Wir starten ganz gemütlich. Hier geht es nur darum, wieder ein Gefühl für Variablen, input() und print() zu bekommen.
Aufgabe: Schreibe ein Programm, das nach dem Namen und dem Geburtsjahr fragt und dann ausgibt:
Hallo Anna, du wirst dieses Jahr 17 Jahre alt.Lösung
name = input("Wie heisst du? ")
geburtsjahr = int(input("In welchem Jahr bist du geboren? "))
alter = 2026 - geburtsjahr
print("Hallo " + name + ", du wirst dieses Jahr " + str(alter) + " Jahre alt.")
Beachte: input() liefert immer einen String. Willst du damit rechnen, musst du ihn mit int() in eine Zahl umwandeln. Umgekehrt musst du beim Zusammenhängen mit + die Zahl mit str() wieder zu einem String machen.
Mit einem f-String geht es kürzer:
print(f"Hallo {name}, du wirst dieses Jahr {alter} Jahre alt.")
Aufgabe: Frage die Länge und die Breite eines Rechtecks ab und gib Fläche und Umfang aus.
Lösung
laenge = float(input("Länge: "))
breite = float(input("Breite: "))
flaeche = laenge * breite
umfang = 2 * (laenge + breite)
print("Fläche:", flaeche)
print("Umfang:", umfang)
Hier verwenden wir float() statt int(), damit auch Kommazahlen wie 3.5 eingegeben werden können.
Aufgabe: Eine Anzahl Sekunden wird eingegeben (z.B. $3'750$). Gib sie im Format
1 h 2 min 30 s aus.
Tipp: Du brauchst die Ganzzahldivision // und den Modulo-Operator %.
Lösung
total = int(input("Sekunden: "))
stunden = total // 3600 # wie viele ganze Stunden stecken drin?
rest = total % 3600 # was bleibt danach übrig?
minuten = rest // 60
sekunden = rest % 60
print(stunden, "h", minuten, "min", sekunden, "s")
Das Paar // und % ist eines der wichtigsten Werkzeuge überhaupt: // liefert, wie oft etwas hineinpasst, % liefert, was übrig bleibt. Wir werden es noch oft brauchen.
Ein Programm soll nicht immer dasselbe tun. Mit if, elif und else treffen wir Entscheidungen.
Aufgabe: Lies eine ganze Zahl ein und gib aus, ob sie
gerade oder
ungerade ist.
Lösung
zahl = int(input("Zahl: "))
if zahl % 2 == 0:
print("gerade")
else:
print("ungerade")
Eine Zahl ist genau dann gerade, wenn sie bei der Division durch $2$ den Rest $0$ lässt. Der Test zahl % 2 == 0 ist der Standardtrick für „gerade“ und wird dir immer wieder begegnen.
Aufgabe: Lies eine ganze Zahl ein und gib aus, ob sie
positiv,
negativ oder
null ist.
Lösung
zahl = int(input("Zahl: "))
if zahl > 0:
print("positiv")
elif zahl < 0:
print("negativ")
else:
print("null")
Wichtig: Sobald ein Zweig zutrifft, werden alle folgenden elif/else übersprungen. Wenn also zahl > 0 wahr ist, wird zahl < 0 gar nicht mehr geprüft.
Aufgabe: Lies drei Zahlen ein und gib die
grösste aus. Verwende dazu nur
if/
elif/
else (kein
max()).
Lösung
a = float(input("1. Zahl: "))
b = float(input("2. Zahl: "))
c = float(input("3. Zahl: "))
if a >= b and a >= c:
print("Die grösste Zahl ist", a)
elif b >= a and b >= c:
print("Die grösste Zahl ist", b)
else:
print("Die grösste Zahl ist", c)
Eine elegantere Variante nach dem Maximum-Muster aus dem Werkzeugkasten – die wird später bei vielen Zahlen unentbehrlich:
maximum = a # a ist vorläufig die grösste
if b > maximum:
maximum = b
if c > maximum:
maximum = c
print("Die grösste Zahl ist", maximum)
Aufgabe: Lies eine Punktzahl zwischen $0$ und $100$ ein und gib die Note nach folgender Tabelle aus:
| Punkte | Note |
| 90–100 | 6 |
| 75–89 | 5 |
| 60–74 | 4 |
| 40–59 | 3 |
| 0–39 | 2 |
Fange auch ungültige Eingaben (kleiner als $0$ oder grösser als $100$) ab.
Lösung
punkte = int(input("Punkte: "))
if punkte < 0 or punkte > 100:
print("Ungültige Eingabe!")
elif punkte >= 90:
print("Note 6")
elif punkte >= 75:
print("Note 5")
elif punkte >= 60:
print("Note 4")
elif punkte >= 40:
print("Note 3")
else:
print("Note 2")
Der Trick bei einer solchen elif-Kette: Wir prüfen von oben nach unten und müssen die untere Grenze jeweils gar nicht mehr testen. Kommt Python zur Zeile elif punkte >= 75:, dann weiss es bereits, dass punkte < 90 gilt.
Aufgabe: Lies ein Jahr ein und gib aus, ob es ein
Schaltjahr ist. Regel:
Teste dein Programm mit $2024$, $1900$, $2000$ und $2023$.
Lösung
Die Reihenfolge ist entscheidend: Wir müssen die speziellste Regel zuerst prüfen.
jahr = int(input("Jahr: "))
if jahr % 400 == 0:
print("Schaltjahr")
elif jahr % 100 == 0:
print("kein Schaltjahr")
elif jahr % 4 == 0:
print("Schaltjahr")
else:
print("kein Schaltjahr")
Ergebnisse: $2024$ Schaltjahr, $1900$ kein Schaltjahr, $2000$ Schaltjahr, $2023$ kein Schaltjahr.
Alternativ mit einer einzigen logischen Bedingung:
if (jahr % 4 == 0 and jahr % 100 != 0) or jahr % 400 == 0:
print("Schaltjahr")
else:
print("kein Schaltjahr")
Eine while-Schleife wiederholt einen Block solange eine Bedingung wahr ist. Drei Dinge gehören immer dazu:
Initialisierung: die Laufvariable vor der Schleife setzen
Bedingung: wann soll weitergemacht werden?
Veränderung: die Laufvariable im Schleifenkörper verändern
Vergisst du Punkt 3, läuft dein Programm ewig. Mit Ctrl + C brichst du eine Endlosschleife ab.
Aufgabe: Gib die Zahlen von $1$ bis $10$ aus, jede auf einer eigenen Zeile.
Lösung
i = 1 # Initialisierung
while i <= 10: # Bedingung
print(i)
i = i + 1 # Veränderung
Verfolge im Kopf, was passiert: i ist zuerst $1$, wird ausgegeben, dann auf $2$ erhöht. Beim Durchgang mit $i = 10$ wird noch ausgegeben, dann wird i auf $11$ gesetzt – und die Bedingung 11 <= 10 ist falsch. Die Schleife endet.
Aufgabe: Lies eine Zahl $n$ ein und mache einen
Countdown von $n$ bis $0$. Am Schluss soll
Start! ausgegeben werden.
Lösung
n = int(input("Von welcher Zahl soll der Countdown starten? "))
while n >= 0:
print(n)
n = n - 1
print("Start!")
Hier zählen wir rückwärts. Entsprechend dreht sich auch die Bedingung um: n >= 0 statt n <= 10.
Aufgabe: Gib alle
Vielfachen von $7$ aus, die kleiner als $100$ sind.
Löse die Aufgabe auf zwei Arten:
mit einer Laufvariablen, die in Siebnerschritten wächst
mit einer Laufvariablen, die in Einerschritten wächst, plus einer Verzweigung
Lösung
Variante 1 – direkt in Siebnerschritten:
v = 7
while v < 100:
print(v)
v = v + 7
Variante 2 – jede Zahl prüfen:
i = 1
while i < 100:
if i % 7 == 0:
print(i)
i = i + 1
Variante 1 ist deutlich effizienter (sie macht 14 statt 99 Durchgänge). Variante 2 ist dafür flexibler: Sie lässt sich sofort auf kompliziertere Bedingungen erweitern, etwa „durch 7 oder durch 5 teilbar“.
Aufgabe: Gib alle
Zweierpotenzen aus, die höchstens $1000$ sind: $1, 2, 4, 8, \ldots$
Lösung
z = 1
while z <= 1000:
print(z)
z = z * 2
Ausgabe: $1, 2, 4, \ldots, 512$. Die Laufvariable muss sich nicht immer um einen festen Betrag ändern – hier wird sie verdoppelt. Dadurch wächst sie sehr schnell, und die Schleife ist nach 10 Durchgängen fertig.
Aufgabe: Gib eine Tabelle der Quadratzahlen und Kubikzahlen von $1$ bis $12$ aus:
n n^2 n^3
1 1 1
2 4 8
3 9 27
...
Tipp: Mit \t erzeugst du einen Tabulator.
Lösung
print("n\tn^2\tn^3")
n = 1
while n <= 12:
print(n, "\t", n**2, "\t", n**3)
n = n + 1
Die Ausgabezeile lässt sich auch mit einem f-String schreiben, was besonders bei fixen Spaltenbreiten schöner aussieht:
print(f"{n:3} {n**2:6} {n**3:6}")
Dabei bedeutet {n:3}, dass die Zahl auf eine Breite von 3 Zeichen rechtsbündig ausgerichtet wird.
Jetzt kommt das wohl wichtigste Muster überhaupt: der Akkumulator. Wir legen eine Variable an, die das Zwischenergebnis speichert, und verändern sie in jedem Schleifendurchgang.
Aufgabe: Lies eine Zahl $n$ ein und berechne die Summe $1 + 2 + 3 + \ldots + n$.
Lösung
n = int(input("n: "))
summe = 0 # noch nichts addiert
i = 1
while i <= n:
summe = summe + i # das ist der Kern des Ganzen
i = i + 1
print("Die Summe ist", summe)
Die Zeile summe = summe + i liest sich anfangs seltsam. In der Mathematik wäre das eine unlösbare Gleichung! In der Programmierung bedeutet = aber zuweisen, nicht „ist gleich“: Berechne die rechte Seite mit dem alten Wert von summe und speichere das Ergebnis als neuen Wert von summe.
Kürzer schreibt man dasselbe als summe += i.
Kontrolle: Für $n = 100$ muss $5050$ herauskommen (Gauss lässt grüssen).
Aufgabe: Berechne die
Fakultät $n! = 1 \cdot 2 \cdot 3 \cdot \ldots \cdot n$.
Lösung
n = int(input("n: "))
produkt = 1 # Startwert 1, NICHT 0!
i = 1
while i <= n:
produkt = produkt * i
i = i + 1
print(n, "! =", produkt)
Der häufigste Fehler hier: produkt = 0 als Startwert. Dann bleibt das Ergebnis für immer $0$. Beim Summieren ist $0$ das neutrale Element, beim Multiplizieren ist es $1$.
Kontrolle: $5! = 120$, $10! = 3'628'800$.
Aufgabe: Berechne die Summe aller
geraden Zahlen von $1$ bis $n$, und gleichzeitig die Summe aller
ungeraden. Gib beide aus.
Lösung
n = int(input("n: "))
summe_gerade = 0
summe_ungerade = 0
i = 1
while i <= n:
if i % 2 == 0:
summe_gerade = summe_gerade + i
else:
summe_ungerade = summe_ungerade + i
i = i + 1
print("gerade: ", summe_gerade)
print("ungerade:", summe_ungerade)
Hier kombinieren wir zum ersten Mal Schleife und Verzweigung: Die Schleife besucht jede Zahl, die Verzweigung entscheidet, welcher Akkumulator erhöht wird. Zwei Akkumulatoren nebeneinander zu führen ist völlig üblich.
Aufgabe: Du legst $1000$ Franken zu $2\%$ Zins pro Jahr an. Nach wie vielen Jahren hast du zum ersten Mal mehr als $2000$ Franken? Gib auch den Kontostand nach jedem Jahr aus.
Lösung
kapital = 1000
jahre = 0
while kapital < 2000:
kapital = kapital * 1.02
jahre = jahre + 1
print("Jahr", jahre, ":", round(kapital, 2))
print("Nach", jahre, "Jahren ist das Kapital verdoppelt.")
Antwort: nach 36 Jahren.
Das ist ein neuer Typ von Schleife! Bisher wussten wir vorher, wie oft sie läuft. Hier wissen wir es nicht – die Schleife läuft, bis eine Bedingung erfüllt ist. Genau dafür ist while gemacht, und genau hier wäre eine for-Schleife das falsche Werkzeug.
Bis jetzt haben wir immer eine feste Anzahl Zahlen verarbeitet. Nun soll der Benutzer selbst entscheiden, wann Schluss ist. Dazu vereinbart man einen Abbruchwert (engl. sentinel), z.B. die $0$.
Aufgabe: Lies so lange Zahlen ein, bis der Benutzer $0$ eingibt. Gib danach die
Summe aller eingegebenen Zahlen aus.
Lösung
summe = 0
zahl = int(input("Zahl (0 = Ende): ")) # erste Eingabe VOR der Schleife
while zahl != 0:
summe = summe + zahl
zahl = int(input("Zahl (0 = Ende): ")) # nächste Eingabe am ENDE der Schleife
print("Summe:", summe)
Dieses Muster heisst „Lies vor der Schleife, lies am Ende der Schleife“. Es sieht unschön aus, weil die input()-Zeile zweimal vorkommt, ist aber der saubere Standardweg: Die Bedingung zahl != 0 kann nur geprüft werden, wenn bereits eine Zahl vorliegt.
Wichtig: Die $0$ selbst wird nicht mitgezählt – sobald sie eingegeben wird, bricht die Schleife ab, bevor sie addiert wird.
Aufgabe: Erweitere das vorige Programm: Gib zusätzlich die
Anzahl der eingegebenen Zahlen und den
Mittelwert aus. Achte darauf, dass dein Programm nicht abstürzt, wenn der Benutzer sofort $0$ eingibt.
Lösung
summe = 0
anzahl = 0
zahl = int(input("Zahl (0 = Ende): "))
while zahl != 0:
summe = summe + zahl
anzahl = anzahl + 1 # der Zähler aus dem Werkzeugkasten
zahl = int(input("Zahl (0 = Ende): "))
print("Anzahl:", anzahl)
print("Summe: ", summe)
if anzahl > 0:
print("Mittelwert:", summe / anzahl)
else:
print("Keine Zahlen eingegeben.")
Der Zähler und der Summierer laufen hier parallel mit. Die abschliessende Verzweigung ist kein Schönheitsfehler, sondern notwendig: Ohne sie würde summe / anzahl bei $0$ Eingaben eine Division durch null verursachen und das Programm abstürzen lassen. Solche Randfälle (engl. edge cases) mitzudenken gehört zum Handwerk.
Aufgabe: Lies wieder Zahlen ein, bis $0$ eingegeben wird. Gib die
grösste und die
kleinste eingegebene Zahl aus. Es dürfen auch negative Zahlen eingegeben werden!
Lösung
zahl = int(input("Zahl (0 = Ende): "))
if zahl == 0:
print("Keine Zahlen eingegeben.")
else:
maximum = zahl # die erste Zahl ist vorläufig grösste UND kleinste
minimum = zahl
while zahl != 0:
if zahl > maximum:
maximum = zahl
if zahl < minimum:
minimum = zahl
zahl = int(input("Zahl (0 = Ende): "))
print("Grösste:", maximum)
print("Kleinste:", minimum)
Der entscheidende Punkt ist die Initialisierung: Wir setzen maximum = 0 nicht! Gibt der Benutzer nur negative Zahlen ein, wäre sonst $0$ das falsche Ergebnis. Der einzig sichere Startwert ist der erste tatsächlich eingegebene Wert.
Beachte auch, dass zwei aufeinanderfolgende if verwendet werden und kein elif. Beim allerersten Durchgang müssen ja beide Vergleiche stattfinden können.
Aufgabe: Programmiere eine Passwortabfrage. Das Passwort lautet
python. Der Benutzer hat
höchstens drei Versuche. Bei richtigem Passwort:
Willkommen!, nach drei Fehlversuchen:
Zugang gesperrt.Lösung
passwort = "python"
versuche = 1
eingabe = input("Passwort: ")
while eingabe != passwort and versuche < 3:
versuche = versuche + 1
eingabe = input("Falsch. Passwort: ")
if eingabe == passwort:
print("Willkommen!")
else:
print("Zugang gesperrt.")
Neu ist hier die zusammengesetzte Bedingung: Die Schleife läuft nur weiter, wenn das Passwort falsch ist und noch Versuche übrig sind. Sobald eine der beiden Bedingungen kippt, ist Schluss.
Nach der Schleife wissen wir aber nicht, welche der beiden Bedingungen zum Abbruch geführt hat. Deshalb müssen wir das mit einem abschliessenden if nachträglich herausfinden. Das ist ein sehr typisches Vorgehen.
Jetzt wird es interessant: Innerhalb der Schleife treffen wir Entscheidungen.
Aufgabe: Wie viele Zahlen von $1$ bis $100$ sind durch $3$ teilbar, aber
nicht durch $5$? Lass das Programm die Antwort berechnen.
Lösung
i = 1
anzahl = 0
while i <= 100:
if i % 3 == 0 and i % 5 != 0:
anzahl = anzahl + 1
i = i + 1
print("Anzahl:", anzahl)
Antwort: 27.
Nachrechnen: Von $1$ bis $100$ gibt es $33$ Vielfache von $3$. Davon sind $6$ auch durch $5$ teilbar (nämlich die Vielfachen von $15$: $15, 30, 45, 60, 75, 90$). Bleiben $33 - 6 = 27$.
Aufgabe: Der Computer denkt sich eine Zufallszahl zwischen $1$ und $100$. Der Benutzer rät. Nach jedem Tipp gibt das Programm
zu klein oder
zu gross aus. Bei richtigem Tipp wird die Anzahl der Versuche ausgegeben.
Lösung
import random
zahl = random.randint(1, 100)
tipp = int(input("Dein Tipp: "))
versuche = 1
while tipp != zahl:
if tipp < zahl:
print("Zu klein!")
else:
print("Zu gross!")
tipp = int(input("Dein Tipp: "))
versuche = versuche + 1
print("Richtig! Du hast", versuche, "Versuche gebraucht.")
Wieder dasselbe Muster wie bei der Eingabeschleife: erste Eingabe vor der Schleife, weitere Eingaben am Ende des Schleifenkörpers.
Übrigens: Wer geschickt rät (immer in der Mitte des verbleibenden Bereichs), braucht nie mehr als 7 Versuche. Denn $2^7 = 128 > 100$. Diese Strategie heisst binäre Suche – sie kommt in der letzten Zusatzaufgabe wieder vor.
Aufgabe (Collatz): Lies eine natürliche Zahl $n > 0$ ein und wende folgende Regel wiederholt an:
Gib jeden Zwischenschritt aus und höre auf, sobald $n = 1$ erreicht ist. Zähle die Schritte.
Probiere danach $n = 27$ aus.
Lösung
n = int(input("Startzahl: "))
schritte = 0
while n != 1:
if n % 2 == 0:
n = n // 2 # Ganzzahldivision, damit kein float entsteht
else:
n = 3 * n + 1
schritte = schritte + 1
print(n)
print("Anzahl Schritte:", schritte)
Für $n = 27$ braucht man 111 Schritte, und zwischendurch klettert die Zahl bis auf $9232$ hinauf – obwohl sie so klein startet.
Das Bemerkenswerte an dieser Aufgabe: Es ist bis heute unbewiesen, ob die Schleife für jede Startzahl überhaupt terminiert. Das ist die berühmte Collatz-Vermutung. Man hat sie für sehr grosse Zahlen per Computer überprüft, aber niemand konnte sie bisher allgemein beweisen. Du schreibst hier also ein Programm, von dem niemand weiss, ob es immer anhält.
Aufgabe: Berechne die
Quersumme einer eingegebenen Zahl. Beispiel: $4271 \rightarrow 4+2+7+1 = 14$.
Tipp: Mit n % 10 bekommst du die letzte Ziffer, mit n // 10 schneidest du sie ab.
Lösung
n = int(input("Zahl: "))
quersumme = 0
while n > 0:
quersumme = quersumme + n % 10 # letzte Ziffer dazuzählen
n = n // 10 # letzte Ziffer abschneiden
print("Quersumme:", quersumme)
Verfolge es an $4271$ Schritt für Schritt:
| Durchgang | n | n % 10 | quersumme |
| Start | 4271 | – | 0 |
| 1 | 427 | 1 | 1 |
| 2 | 42 | 7 | 8 |
| 3 | 4 | 2 | 10 |
| 4 | 0 | 4 | 14 |
Sobald n den Wert $0$ erreicht, ist die Zahl „aufgebraucht“ und die Schleife endet. Diese Technik, eine Zahl ziffernweise zu zerlegen, ist extrem nützlich – merke sie dir gut.
Aufgabe: Drehe eine eingegebene Zahl um: $4271 \rightarrow 1724$. Das Ergebnis soll eine
Zahl sein, kein String.
Lösung
n = int(input("Zahl: "))
rueckwaerts = 0
while n > 0:
ziffer = n % 10
rueckwaerts = rueckwaerts * 10 + ziffer # bisheriges Ergebnis "nach links schieben"
n = n // 10
print("Umgekehrt:", rueckwaerts)
Der Trick steckt in rueckwaerts * 10 + ziffer: Die Multiplikation mit $10$ schiebt alle bisherigen Ziffern eine Stelle nach links und macht rechts Platz für die neue.
Ablauf für $4271$: $0 \rightarrow 1 \rightarrow 17 \rightarrow 172 \rightarrow 1724$.
Zusatz: Baue das Programm so um, dass es prüft, ob eine Zahl ein Palindrom ist (vorwärts wie rückwärts gleich, z.B. $12321$). Du musst dir dazu die ursprüngliche Zahl in einer zweiten Variablen merken, bevor du sie in der Schleife zerstörst!
Aufgabe: Lies eine Zahl $n \geq 2$ ein und prüfe, ob sie eine
Primzahl ist.
Lösung
n = int(input("Zahl: "))
ist_prim = True # Flag: wir gehen erst mal davon aus, dass es eine Primzahl ist
teiler = 2
while teiler < n:
if n % teiler == 0:
ist_prim = False # ein Teiler gefunden -> doch keine Primzahl
teiler = teiler + 1
if ist_prim:
print(n, "ist eine Primzahl.")
else:
print(n, "ist keine Primzahl.")
Das ist die erste Anwendung des Flag-Musters: Eine Wahrheitswert-Variable merkt sich, ob während der Schleife etwas passiert ist. Nach der Schleife wird sie ausgewertet.
Verbesserung: Man muss nur bis zur Wurzel von $n$ testen. Denn wenn $n = a \cdot b$ mit $a \leq b$, dann ist zwingend $a \leq \sqrt{n}$. Statt math.sqrt zu verwenden, quadriert man einfach den Teiler:
while teiler * teiler <= n:
if n % teiler == 0:
ist_prim = False
teiler = teiler + 1
Für $n = 1'000'003$ sind das statt einer Million nur noch rund tausend Durchgänge. Ein spürbarer Unterschied!
Aufgabe: Gib
alle Primzahlen bis $100$ aus. Du brauchst dazu zwei ineinander verschachtelte while-Schleifen.
Lösung
n = 2 # äussere Schleife: die Kandidaten
while n <= 100:
ist_prim = True
teiler = 2 # innere Schleife: die möglichen Teiler
while teiler * teiler <= n:
if n % teiler == 0:
ist_prim = False
teiler = teiler + 1
if ist_prim:
print(n, end=" ")
n = n + 1
Ausgabe: 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97
Der wichtigste Punkt bei verschachtelten Schleifen: Die Variablen der inneren Schleife (teiler und ist_prim) müssen innerhalb der äusseren Schleife zurückgesetzt werden. Setzt man sie versehentlich ganz nach oben, funktioniert nur der erste Durchgang – ein Klassiker unter den Fehlern.
Aufgabe: Berechne den
grössten gemeinsamen Teiler zweier Zahlen mit dem euklidischen Algorithmus:
Ersetze wiederholt das Paar $(a, b)$ durch $(b,\ a \bmod b)$, bis $b = 0$ ist. Dann ist $a$ der ggT.
Lösung
a = int(input("a: "))
b = int(input("b: "))
while b != 0:
rest = a % b
a = b
b = rest
print("Der ggT ist", a)
Die Hilfsvariable rest ist unverzichtbar: Würde man direkt a = b schreiben, wäre der alte Wert von a verloren, bevor man ihn für a % b braucht. Dieses Zwischenspeichern beim Vertauschen ist eine Standardtechnik.
Ablauf für $a = 48$, $b = 18$: $(48,18) \rightarrow (18,12) \rightarrow (12,6) \rightarrow (6,0)$. Ergebnis: $6$.
Der Algorithmus ist über 2000 Jahre alt und immer noch der schnellste bekannte Weg – er stammt aus Euklids Elementen.
Aufgabe: Berechne die
Quadratwurzel einer positiven Zahl $x$ mit dem Heron-Verfahren, ohne
math.sqrt zu verwenden.
Starte mit einer Näherung $g = x/2$ und verbessere sie wiederholt mit
$$g_{\text{neu}} = \frac{1}{2}\left(g + \frac{x}{g}\right)$$
bis sich der Wert kaum mehr ändert.
Lösung
x = float(input("Zahl: "))
g = x / 2
schritte = 0
while abs(g * g - x) > 0.000001: # solange das Quadrat noch zu weit weg ist
g = (g + x / g) / 2
schritte = schritte + 1
print(schritte, ":", g)
print("Wurzel aus", x, "ist ungefähr", g)
Für $x = 2$ ist man nach vier Schritten schon bei $1.41421356\ldots$ – das Verfahren verdoppelt die Anzahl korrekter Stellen mit jedem Schritt.
Zwei Dinge sind hier wichtig:
Aufgabe: Wandle eine eingegebene Dezimalzahl in ihre
Binärdarstellung um, ohne
bin() zu verwenden.
Tipp: Teile wiederholt durch $2$ und sammle die Reste ein – aber achte auf die Reihenfolge!
Lösung
n = int(input("Dezimalzahl: "))
binaer = ""
while n > 0:
binaer = str(n % 2) + binaer # neue Ziffer VORNE anfügen
n = n // 2
if binaer == "": # Sonderfall: die Eingabe war 0
binaer = "0"
print("Binär:", binaer)
Der springende Punkt ist str(n % 2) + binaer statt binaer + str(n % 2): Die Reste fallen bei der Division von hinten nach vorne an, also müssen sie vorne angehängt werden.
Ablauf für $13$: Reste $1, 0, 1, 1$ – der String wächst als "1", "01", "101", "1101". Und tatsächlich ist $13 = 8 + 4 + 1 = 1101_2$.
Der Sonderfall $n = 0$ muss extra behandelt werden, weil die Schleife dann gar nie durchlaufen wird und der String leer bleibt.
Aufgabe: Gib mit verschachtelten while-Schleifen folgende Figuren aus. Die Höhe $n$ wird eingelesen.
* Figur 1: ***** Figur 2: *
** **** ***
*** *** *****
**** ** *******
***** *
Lösung
Figur 1 – wachsendes Dreieck, ganz ohne innere Schleife:
n = int(input("Höhe: "))
zeile = 1
while zeile <= n:
print("*" * zeile)
zeile = zeile + 1
Figur 2 – schrumpfendes Dreieck:
zeile = n
while zeile >= 1:
print("*" * zeile)
zeile = zeile - 1
Figur 3 – die Pyramide braucht zusätzlich führende Leerzeichen. In Zeile $z$ stehen $n - z$ Leerzeichen und $2z - 1$ Sterne:
zeile = 1
while zeile <= n:
print(" " * (n - zeile) + "*" * (2 * zeile - 1))
zeile = zeile + 1
Wer den Trick "*" * zeile nicht verwenden darf, baut die Zeile mit einer inneren Schleife zusammen:
zeile = 1
while zeile <= n:
ausgabe = ""
spalte = 1
while spalte <= zeile:
ausgabe = ausgabe + "*"
spalte = spalte + 1
print(ausgabe)
zeile = zeile + 1
Das ist wieder der Akkumulator – diesmal aber mit einem String statt einer Zahl.
Aufgabe: Gib die kleine Einmaleins-Tabelle ($1$ bis $10$) als sauberes Gitter aus.
Lösung
zeile = 1
while zeile <= 10:
ausgabe = ""
spalte = 1
while spalte <= 10:
ausgabe = ausgabe + str(zeile * spalte) + "\t"
spalte = spalte + 1
print(ausgabe)
zeile = zeile + 1
Zwei Beobachtungen:
Alternativ kann man auch direkt ausgeben und den Zeilenumbruch unterdrücken:
while spalte <= 10:
print(zeile * spalte, end="\t")
spalte = spalte + 1
print() # leeres print() erzeugt den Zeilenumbruch
Aufgabe (Knacknuss): Ein Bauer hat Hühner und Kaninchen. Zusammen haben die Tiere $35$ Köpfe und $94$ Beine. Wie viele Tiere jeder Sorte hat er? Löse die Aufgabe mit einer
while-Schleife durch systematisches Ausprobieren – nicht mit einem Gleichungssystem.
Lösung
koepfe = 35
beine = 94
huehner = 0
while huehner <= koepfe:
kaninchen = koepfe - huehner # der Rest muss Kaninchen sein
if 2 * huehner + 4 * kaninchen == beine:
print("Hühner:", huehner, " Kaninchen:", kaninchen)
huehner = huehner + 1
Ergebnis: 23 Hühner und 12 Kaninchen.
Diese Technik heisst Brute Force (rohe Gewalt): Man probiert einfach alle Möglichkeiten durch und prüft jede. Für einen Menschen wäre das mühsam, für einen Computer sind $36$ Durchgänge ein Klacks.
Beachte den Trick, dass nur eine Grösse durchprobiert wird – die zweite ergibt sich aus der Kopfzahl. Mit zwei verschachtelten Schleifen ginge es auch, wäre aber unnötig aufwendig.
Wer alles Bisherige gelöst hat, darf sich hier austoben. Ab jetzt sind Listen erlaubt. Die wichtigsten Befehle:
liste = [] # leere Liste
liste = [3, 1, 4, 1, 5] # Liste mit Startwerten
liste.append(9) # Element hinten anfügen
liste[0] # erstes Element (Zählung beginnt bei 0!)
liste[-1] # letztes Element
len(liste) # Anzahl Elemente
liste[2] = 7 # Element überschreiben
liste.pop(1) # Element an Position 1 entfernen
Aufgabe: Lies Zahlen ein, bis $0$ eingegeben wird, und speichere sie in einer
Liste. Gib danach aus: Anzahl, Summe, Mittelwert, Maximum und Minimum – und zwar alles selbst berechnet mit while-Schleifen, ohne
sum(),
max() oder
min().
Lösung
zahlen = []
zahl = int(input("Zahl (0 = Ende): "))
while zahl != 0:
zahlen.append(zahl)
zahl = int(input("Zahl (0 = Ende): "))
if len(zahlen) == 0:
print("Keine Zahlen eingegeben.")
else:
summe = 0
maximum = zahlen[0]
minimum = zahlen[0]
i = 0
while i < len(zahlen):
summe = summe + zahlen[i]
if zahlen[i] > maximum:
maximum = zahlen[i]
if zahlen[i] < minimum:
minimum = zahlen[i]
i = i + 1
print("Anzahl: ", len(zahlen))
print("Summe: ", summe)
print("Mittelwert:", summe / len(zahlen))
print("Maximum: ", maximum)
print("Minimum: ", minimum)
Achte auf die Bedingung i < len(zahlen) mit echt kleiner: Bei einer Liste mit $5$ Elementen sind die gültigen Indizes $0$ bis $4$. Ein <= würde einen Zugriffsfehler auslösen – der berühmte off-by-one error.
Der Vorteil der Liste: Wir können die Zahlen mehrfach durchgehen. Ohne Liste ist jede Zahl nach der Verarbeitung für immer weg.
Aufgabe: Sortiere eine Liste von Zahlen mit
Bubble Sort: Gehe die Liste wiederholt durch und vertausche benachbarte Elemente, wenn sie in der falschen Reihenfolge stehen. Wiederhole, bis kein Tausch mehr nötig war.
Lösung
zahlen = [5, 2, 9, 1, 7, 3]
getauscht = True # Flag
while getauscht:
getauscht = False # Annahme: diesmal ist alles sortiert
i = 0
while i < len(zahlen) - 1:
if zahlen[i] > zahlen[i + 1]:
merker = zahlen[i] # Vertauschen mit Hilfsvariable
zahlen[i] = zahlen[i + 1]
zahlen[i + 1] = merker
getauscht = True # doch nicht sortiert!
i = i + 1
print(zahlen) # Zwischenstand anschauen
print("Sortiert:", zahlen)
Das Flag getauscht steuert hier die äussere Schleife. Solange in einem Durchgang noch getauscht wurde, kann die Liste nicht sortiert sein. Ging ein ganzer Durchgang ohne Tausch vorbei, sind wir fertig.
In Python kann man übrigens eleganter tauschen: zahlen[i], zahlen[i+1] = zahlen[i+1], zahlen[i]. Die Variante mit dem Merker funktioniert dafür in jeder Programmiersprache – und du solltest sie kennen.
Aufgabe (Sieb des Eratosthenes): Finde alle Primzahlen bis $100$, ohne eine einzige Division durchzuführen.
Idee: Schreibe alle Zahlen von $2$ bis $100$ auf. Nimm die erste unmarkierte Zahl – sie ist prim. Streiche danach alle ihre Vielfachen. Wiederhole.
Lösung
n = 100
ist_prim = [True] * (n + 1) # ist_prim[k] sagt, ob k prim ist
ist_prim[0] = False
ist_prim[1] = False
p = 2
while p * p <= n:
if ist_prim[p]:
vielfaches = p * p # alles Kleinere wurde schon gestrichen
while vielfaches <= n:
ist_prim[vielfaches] = False
vielfaches = vielfaches + p # ADDIEREN, nicht dividieren!
p = p + 1
k = 2
while k <= n:
if ist_prim[k]:
print(k, end=" ")
k = k + 1
Der Clou ist die Zeile vielfaches = vielfaches + p: Statt für jede Zahl zu prüfen, ob sie durch $p$ teilbar ist, springen wir direkt von Vielfachem zu Vielfachem. Das Sieb ist deshalb dramatisch schneller als das Durchprobieren aller Teiler.
Warum startet die innere Schleife bei $p \cdot p$ und nicht bei $2p$? Weil alle kleineren Vielfachen von $p$ bereits einen kleineren Primfaktor haben und darum schon gestrichen wurden. Zum Beispiel wurde $3 \cdot 5 = 15$ schon beim Sieben mit $3$ erwischt.
Aufgabe (Umgekehrtes Zahlenraten): Jetzt denkt
du dir eine Zahl zwischen $1$ und $1000$, und der
Computer rät. Nach jedem Tipp antwortest du mit
k (zu klein),
g (zu gross) oder
r (richtig).
Programmiere den Computer so, dass er möglichst wenige Versuche braucht.
Lösung
unten = 1
oben = 1000
versuche = 0
antwort = ""
while antwort != "r":
tipp = (unten + oben) // 2 # immer die Mitte des Bereichs!
versuche = versuche + 1
print("Ist es", tipp, "?")
antwort = input("k = zu klein, g = zu gross, r = richtig: ")
if antwort == "k":
unten = tipp + 1 # untere Hälfte ausschliessen
elif antwort == "g":
oben = tipp - 1 # obere Hälfte ausschliessen
print("Gefunden nach", versuche, "Versuchen!")
Das ist die binäre Suche. Bei jedem Tipp halbiert sich der verbleibende Bereich: $1000 \rightarrow 500 \rightarrow 250 \rightarrow 125 \rightarrow \ldots$ Nach höchstens 10 Versuchen ist die Zahl gefunden, denn $2^{10} = 1024 > 1000$.
Zum Vergleich: Würde der Computer stur $1, 2, 3, \ldots$ durchprobieren, bräuchte er im Mittel $500$ Versuche. Ein gut gewählter Algorithmus schlägt rohe Rechenkraft um Längen.
Zum Weiterdenken: Was passiert, wenn du beim Antworten schummelst? Der Bereich schrumpft dann irgendwann auf nichts zusammen, und unten wird grösser als oben. Ein robustes Programm würde das erkennen und dich des Betrugs bezichtigen.