Zum Hauptinhalt springen

Das Monty-Hall-Dilemma/Ziegenproblem in 10 Python-Zeilen

Hintergrund zum Dilemma

So manch einer erinnert sich an die Spielshow "Geh aufs Ganze" aus den 90ern, in der Kandidaten sich für eines von drei Toren entscheiden mussten. Hinter einem Tor war stets der Preis versteckt und hinter den anderen Toren waren Nieten in Form des Zonk, bzw. in den USA bei Moderator Monty Hall waren es Ziegen. Der Kandidat wählt zu Beginn immer ein Tor aus, hinter dem er den Preis vermutet. Der Moderator kann anschließend versuchen, ihn auf andere Tore mit Geldangeboten umzustimmen. Er kann auch Tore öffnen, um die Spannung zu erhöhen.

Dieses Öffnen von Toren fordert den Kandidaten auf, bei seiner bisherigen Wahl zu bleiben oder auf das letzte verbleibende Tor zu wechseln. Genau hier liegt der Kern des Dilemmas. Intuitiv vermutet die Mehrheit, dass es egal ist und die Gewinnchance beide Male bei einem Drittel bleibt. Tatsächlich verdoppelt sich die Gewinnchance auf zwei Drittel, wenn man wechselt. Eigentlich ist es ja ganz einfach, denn wenn ich nicht wechsle, ist es genauso, als ob man mein Tor sofort geöffnet hätte, also ein Drittel. Die verbleibende Alternative muss zwei Drittel Chance besitzen, denn es muss insgesamt auf 100 %, also drei Drittel aufgehen.

Die Simulation

Als ich es vor über 10 Jahren zum ersten Mal gelesen habe, wollte ich es erst nicht so recht glauben, drum habe ich es einfach in Java als Simulation programmiert und wurde schließlich von den Fakten eines Besseren belehrt. In diesem Blog-Eintrag möchte ich das konkrete Beispiel nutzen, um so eine Simulation mit Python zu demonstrieren, und zwar so schlank wie möglich.
Hier zunächst der Code:

from random import randint, sample
numberDoors, trials, results = 5, 1000000, {'stayed':0,'switched':0}
for i in range(trials):
   doors=set(range(1,numberDoors+1))
   doorPlayer, doorPrize = sample(doors,1).pop(), sample(doors,1).pop()
   doorOpened    = sample(doors.difference(set((doorPlayer, doorPrize))),1).pop()
   doorAlternate = sample(doors.difference(set((doorOpened, doorPlayer))),1).pop()
   results['stayed']   += 1 if doorPlayer    == doorPrize else 0
   results['switched'] += 1 if doorAlternate == doorPrize else 0
print (results,results['switched']/(results['stayed']+results['switched']))

Ich finde es beeindruckend, dass Python so eine Simulation in nur 10 Zeilen und relativ lesbar ermöglicht. Ich stütze mich hierbei auf die etwas vernachlässigten SETs in Python. Diese können in Programmierungen zu Datenauswertungen sehr hilfreich sein, weil sie performant das Abgleichen von Mengen ermöglichen. SETs sind in Python Anordnungen wie Listen oder Tupel mit der Ausnahme, dass sie jeden Wert nur einmal beinhalten dürfen und die Reihenfolge zweitrangig ist.
Hier die Erläuterung des Codes:

  1. Import von Funktionen für Zufallsziehungen
  2. Initialisieren und Befüllen von der Anzahl Tore, der Anzahl Versuchsdurchläufe und der Ergebnisablage
  3. Schleifenrumpf
  4. Erzeugen eines Sets mit den Nummern der Tore
  5. Zufallsziehung des Tores mit dem Preis und des Tores, das der Spieler wählt. Die sample-Funktion zieht aus dem Tor-Set ein subset der Länge 1, von dem wir uns den einzigen Wert mit pop() herausziehen, um ihn als Integer zu erhalten.
  6. Zufallsziehung des vom Spielmeister geöffneten Tores. Dafür muss zunächst das Set der bisherigen Tore vom Set aller Tore abgezogen werden.
  7. Bestimmung des verbleibenden Tores
  8. Bewertung des Ergebnisses, falls man bei seiner Wahl bleibt. Hierfür verwende ich den sogenannten ternären Operator. Das ermöglicht einfache Entscheidungen zwischen zwei Alternativen ohne einen eigenen If-Block.
  9. Bewertung des Ergebnisses, falls man wechselt
  10. Darstellung der Ergebnisse und Berechnung der Gewinnwahrscheinlichkeit beim Wechsel

Das Ergebnis liegt nahe 0,667 %, also bei zwei Drittel. Nun kann man sich fragen, warum ich in Zeile 7 so aufwendig die verbleibende Tür per Zufall gezogen habe, wenn es doch eh nur noch eine gibt. Nun, ich habe das Problem hier nicht nur in 10 Zeilen simuliert, sondern auch noch die Möglichkeit geschaffen, das Problem in einem Spiel mit 4 oder mehr Türen zu simulieren. Das Ergebnis: Bei 4 Toren ist das Ergebnis 0,60 %, bei 5 Toren 0,57 %. Mit steigender Anzahl sinkt erwartungsgemäß der Nutzen durch den Torwechsel, da die Restwahrscheinlichkeit sich auf mehrere Alternativen aufteilt. 

Stefan Seltmann
Dein Ansprechpartner
Stefan Seltmann
Lead Expert
Stefan liebt das Programmieren, vor allem rund um Data Engineering und Data Science, und arbeitet quasi in seinem Hobby. Gerade für Softwareentwicklung mit Python und/oder Spark punktet er als b.telligents Telefonjoker.
#CodeFirst, #TestMore, #CodeDoctor