Ein Lottozahlen-Tipp
Als nächste Aufgabe soll ein Programm geschrieben werden, das einen Tipp für die nächste Lottoziehung abgibt. Wer weiß – vielleicht ist das der Gewinnertipp für die Million!
Beim Lotto gibt es 49 kleine Bälle, nummeriert von 1 bis 49, von denen nacheinander 6 gezogen werden. Wer die richtigen 6 Zahlen vorher getippt hatte, gewinnt die Million – vereinfacht gesagt.
Also: Das Programm soll nacheinander 6 zufällige Zahlen ermitteln, die zwischen 1 und 49 liegen, und diese anzeigen. Nichts leichter als das, könnte man denken und folgendes Miniprogramm schreiben:
import random
repeat 6:
print random.randint(1,49)
Leider funktioniert das so nicht wirklich sicher. Es kann ja zum Beispiel Folgendes passieren:
Abbildung 11.1 Die Zahl 40 wurde zwei Mal gezogen.
Dass eine Zahl mehrfach vorkommt, geht natürlich gar nicht. Im wirklichen Lotto ist es unmöglich. Du musst also einen Weg finden, der sicherstellt, dass eine Zahl nicht doppelt gezogen wird. Und – oh Wunder – die Lösung liegt darin, hier auch mit einer Liste zu arbeiten.
Hast du eine Idee, wie man vorgehen könnte? Ich sage gleich vorweg, dass es mindestens drei Möglichkeiten gibt, wie man an das Problem herangehen kann. Denk mal drüber nach!
Methode Nr. 1: Prüfen und bei Bedarf wiederholen
Die erste Methode ist folgende: Es werden nacheinander 6 Zahlen zwischen 1 und 49 gezogen und an eine Liste angehängt. Jede Zahl wird nach der Ziehung anhand der Liste überprüft und wenn sie dort bereits vorhanden ist, so oft neu gezogen und mit der Liste verglichen, bis sie nicht in der Liste und somit keine doppelte Zahl ist. Das Programm dazu könnte so aussehen:
import random
lottotipp = []
repeat 6:
zahl = random.randint(1,49)
while zahl in lottotipp:
zahl = random.randint(1,49)
lottotipp.append(zahl)
print lottotipp
Teste es – und du kannst es laufen lassen, so oft du willst: Es wird keine doppelten Zahlen mehr geben. Mit der while-Abfrage wird bei jeder Ziehung sichergestellt, dass die gezogene Zahl nicht schon vorhanden ist, wenn doch, wird sogleich eine neue gezogen.
Es funktioniert also, aber es ist nicht elegant programmiert. Warum nicht? Weil man bei dieser Methode nicht vorher weiß, wie oft neu gezogen werden muss, bis eine Zahl kommt, die noch nicht vorhanden ist. Theoretisch könnten immer wieder Zahlen gezogen werden, die es schon gibt, und das Programm würde ewig brauchen (auch wenn das in der Praxis wegen der Verteilung der Zufallszahlen wahrscheinlich kaum passiert). Das Programm tut also so, als würde man nach jeder Ziehung den gezogenen Ball wieder hineinwerfen, und muss dann jedes Mal überprüfen, ob die neu gezogene Zahl schon mal gezogen wurde. Der Ablauf des Programms ist also nicht sauber vorhersagbar.
Methode Nr. 2: Den echten Vorgang simulieren
Warum nicht einfach so vorgehen, wie es der Wirklichkeit am nächsten kommt? Das heißt, wir brauchen als Erstes einen großen Eimer, in dem alle Zahlenbälle von 1 bis 49 drin sind. Das machen wir natürlich statt mit einem Eimer mit einer Liste, die alle Zahlen von 1 bis 49 enthält. Wir können dazu den range-Befehl verwenden:
gesamtliste = range (1,50)
Nun ziehen wir eine dieser Zahlen, anschließend löschen wir diese Zahl aus dem »Gesamttopf« (der Liste), und dann ziehen wir die nächste. Eine Zahl kann nicht doppelt sein, weil die schon gezogenen ja nicht mehr in der Liste sind. Wie in echt eben.
Worauf man dabei achten muss: Bei jeder Ziehung ist die Anzahl der möglichen Elemente um eins niedriger, weil ja immer eine Zahl fehlt. (Wie in echt auch: Bei der ersten Ziehung wird eine Zahl aus 49 möglichen gezogen, bei der zweiten eine aus 48, dann eine aus 47 usw.) Die Anzahl der noch vorhandenen Zahlen in der gesamtliste ermitteln wir immer mit len(gesamtliste).
So würde das Programm aussehen:
import random
gesamtliste = range (1,50)
lottotipp = []
repeat 6:
zahl = random.randint(1,len(gesamtliste))
lottotipp.append(gesamtliste[zahl-1])
del(gesamtliste[zahl-1])
print lottotipp
Auch das kannst du probieren. Es läuft im Zweifelsfall etwas schneller als das erste Programm (auch wenn das kaum spürbar wird). Vor allem aber ist es sicher und garantiert nach sechs glatten Durchläufen zu Ende und produziert trotzdem keine doppelten Zahlen.
Oft lohnt es sich, den Originalablauf einer Aufgabe mit den Mitteln von Python nachzuspielen.
Aber es gibt noch eine dritte Methode, die ich zeigen möchte und die am cleversten ist, weil sie das kürzeste Programm erfordert.
Methode Nr. 3: Mit cleveren Tricks arbeiten
Die dritte Methode geht schlicht und einfach so: Es wird wieder eine Liste aller verfügbaren Zahlen von 1 bis 49 mit der range-Funktion angelegt. Diese wird dann mit dem random.shuffle-Befehl zufällig durchgemischt. Dann werden einfach die ersten 6 Zahlen dieser Liste als Lottozahlen genommen (es könnten auch die letzten sein oder welche aus der Mitte. Nach dem Mischen sind sie alle gleich zufällig angeordnet).
Das Programm dazu sieht so aus:
import random
gesamtliste = range(1,50)
random.shuffle(gesamtliste)
lottotipp = gesamtliste[0:6]
print lottotipp
Ebenso einfach wie raffiniert also, ohne irgendwelche Schleifen oder Abfragen. Es wird eine Liste aller Zahlen von 1 bis 49 erstellt, diese wird bunt gemischt, und die ersten 6 Zahlen werden als Lottotipp angezeigt. Es kann keine Doppelten geben, da ja jede Zahl nur einmal in der gesamtliste vorkommt, und trotzdem erhält man jedes Mal einen anderen zufälligen Lottotipp mit 6 Zahlen von 1 bis 49. Manchmal muss man erst in anderen Bahnen denken, bevor man die optimale Methode für einen Vorgang beim Programmieren gefunden hat. Der optimale Algorithmus ist meistens der, der am einfachsten zu programmieren ist oder am schnellsten abläuft – oder beides.
Methode Nr. 4: Praktische eingebaute Funktionen von »random« verwenden
Und nun, wo wir den optimalen Algorithmus gefunden haben, stellt sich heraus, dass es noch einfacher geht. Für viele Vorgänge gibt es in den Modulen von Python nämlich schon existierende Funktionen, die man einfach nur benutzen muss! So zum Beispiel auch für die Ziehung mehrerer zufälliger Zahlen aus einer Liste. Es gibt im Modul random nämlich einen Befehl, der heißt sample(liste, anzahl) – dieser Befehl wählt automatisch eine Anzahl von zufällig gezogenen, verschiedenen Zahlen aus einer Liste aus. Genau das, was wir brauchen!
Unser Programm verkürzt sich noch weiter.
import random
gesamtliste = range(1,50)
lottotipp = random.sample(gesamtliste,6)
print lottotipp
Sehr praktisch, oder? Und man kann es noch weiter zusammenfassen:
import random
lottotipp = random.sample(range(1,50),6)
print lottotipp
Wenn man die Zahlen auch noch der Größe nach sortiert ausgeben möchte, ist nur ein weiterer Listen-Befehl vor dem print einzufügen.
lottotipp.sort()
Das sorgt dafür, dass die Liste der Lottozahlen von klein nach groß sortiert und ausgegeben wird.
Abbildung 11.2 Ein guter sortierter Lottotipp – vielleicht 6 Richtige?