Seite 1 von 1

Ziegenproblem

Verfasst: Donnerstag 28. Januar 2010, 20:48
von fledermausland
Nachtrag: das hätte wohl besser in 'Codesnippets' gepasst, hoppla

Hey ho

ich hatte vorhin mal wieder etwas über das allbekannte Ziegenproblem gelesen, und darum ein kurzes Simulationsscript erstellt. Die Auskommentierten Sachen waren ursprünglich dazu da, um den Spielverllauf nachzuvollziehen, aber bei mehr als 100 Durchgängen ist es dann auch überflüssig.

Code: Alles auswählen

import random

def mixlist(data):
    ''' Mix the incoming list up '''
    size = len(data)
    a =  []
    while size:
        size = size - 1
        index = random.randint(0, size)
        elem = data[index]
        data[index] = data[size]
        a.append(elem)
    return a

def game(n, switch):
    game_gates = mixlist(['Car','Goat','Goat'])
    for i in xrange(n):        
        # print '1\t2\t3'
        # print '%s\t%s\t%s' % (game_gates[0], game_gates[1], game_gates[2])
        cand_choice = random.randint(1,3)
        # print 'Candidate chooses gate %s' % cand_choice
        mod_choice = cand_choice
        while (mod_choice == cand_choice) or (game_gates[mod_choice - 1] == 'Car'):
            # Just find a gate with a goat which is not chosen by candidate
            mod_choice = random.randint(1,3)
        # print 'Moderator opens gate %s' % mod_choice
        # if not switch:
            # print 'Candidate stays with gate %s' % cand_choice
        if switch:
            i = 1
            while (i == cand_choice) or (i == mod_choice):
                # Find the only gate left to choose
                i += 1
            cand_choice = i            
            # print 'Candidate switches to gate %s' % cand_choice
        if game_gates[cand_choice - 1] == 'Car':
            # print 'The candidate won\n'
            yield 1
        else:
            # print 'The candidate lost\n'
            yield 0

def testrun():
    n = raw_input('How many games shall be simulated? [100] :')
    try:
        n = int(n)
    except:
        print 'Not a valid number, using default. [100]'
        n = 100
    if n < 1:
        n = 100
    won_stay = game(n, 0)
    won_stay = sum(won_stay)
    won_switch = game(n, 1)
    won_switch = sum(won_switch)
    print 'The candidate won %s of %s rounds without switching. (%.2f percent)' % \
            (won_stay, n, won_stay*100.0/float(n))
    print 'The candidate won %s of %s rounds with switching. (%.2f percent)' % \
            (won_switch, n, won_switch*100.0/float(n))

testrun()
Besonders interessant, wie sich das Ergebnis bei immer größeren Zahlen an die mathemathische Wahrscheinlichkeit annähert (1/3 ohne Wechsel, 2/3 mit)

Code: Alles auswählen

user@l:~/testing$ python ziege.py 
How many games shall be simulated? [100] :12
The candidate won 3 of 12 rounds without switching. (25.00 percent)
The candidate won 9 of 12 rounds with switching. (75.00 percent)

user@l:~/testing$ python ziege.py 
How many games shall be simulated? [100] :123
The candidate won 38 of 123 rounds without switching. (30.89 percent)
The candidate won 80 of 123 rounds with switching. (65.04 percent)

user@l:~/testing$ python ziege.py 
How many games shall be simulated? [100] :1234
The candidate won 416 of 1234 rounds without switching. (33.71 percent)
The candidate won 816 of 1234 rounds with switching. (66.13 percent)

user@l:~/testing$ python ziege.py 
How many games shall be simulated? [100] :12345
The candidate won 4188 of 12345 rounds without switching. (33.92 percent)
The candidate won 8250 of 12345 rounds with switching. (66.83 percent)

user@l:~/testing$ python ziege.py 
How many games shall be simulated? [100] :123456
The candidate won 41315 of 123456 rounds without switching. (33.47 percent)
The candidate won 82413 of 123456 rounds with switching. (66.75 percent)

user@l:~/testing$ python ziege.py 
How many games shall be simulated? [100] :1234567
The candidate won 410350 of 1234567 rounds without switching. (33.24 percent)
The candidate won 822155 of 1234567 rounds with switching. (66.59 percent)

user@l:~/testing$ python ziege.py 
How many games shall be simulated? [100] :12345678
The candidate won 4117087 of 12345678 rounds without switching. (33.35 percent)
The candidate won 8226502 of 12345678 rounds with switching. (66.63 percent)
Sinnfrei, aber nicht uninteressant.

Verfasst: Donnerstag 28. Januar 2010, 21:19
von jbs
Habs mal überarbeitet:

Code: Alles auswählen

from random import shuffle, randrange

def game(n, switch):
    gates = [1, 0, 0]
    shuffle(gates)

    for i in xrange(n):       
        candidate = randrange(0,3)
        for index, gate in enumerate(gates):
            if not gate and index!=candidate:
                moderator = index
        if switch:
            candidate = 3 - candidate - moderator
        if gates[candidate]:
            yield True
        else:
            yield False

def testrun():
    default = 100
    n = 0
    try:
        n = int(raw_input('How many games shall be simulated? [%s] :' %default))
    except ValueError:
        print 'Not a valid number, using default. [%s]' %default
    n = n if n > 0 else default
    
    won_stay = sum(game(n, switch=False))
    won_switch = sum(game(n, switch=True))
    print 'The candidate won %s of %s rounds without switching. (%.2f percent)' % \
            (won_stay, n, won_stay*100.0/float(n))
    print 'The candidate won %s of %s rounds with switching. (%.2f percent)' % \
            (won_switch, n, won_switch*100.0/float(n))

if __name__ == '__main__':
    testrun()
Ein paar Sachen waren da noch, die nicht so optimal sind (e.g. die while-Schleifen). Warum mischt du die Liste, wenn du eh zufällige Auswahlen hast?
Ich hab es trotzdem drin gelassen.

Verfasst: Donnerstag 28. Januar 2010, 23:14
von fledermausland
Ah, da gabs ja wirklich einiges zu verbessern, danke. Zu dem Mischen am Anfang: es sollte eine möglichst originalgetreue Simulation sein, egal ob das sinnvoll ist oder nicht. Und im "Original" war es ja auch dem Zufall überlassen, was zu Anfang hinter welchem Tor ist.

Verfasst: Donnerstag 28. Januar 2010, 23:16
von EyDu
Na da würde ich noch einmal nachdenken: ;-)

Code: Alles auswählen

if gates[candidate]:
    yield True
else:
    yield False 

Verfasst: Donnerstag 28. Januar 2010, 23:23
von fledermausland
Ich nehme an, du meinst, dass man sich das "yield False" auch schenken kann?

Verfasst: Donnerstag 28. Januar 2010, 23:23
von jbs
Oh, was ein fauxpas.

Verfasst: Donnerstag 28. Januar 2010, 23:24
von jbs
fledermausland hat geschrieben:Ich nehme an, du meinst, dass sich das "yield False" auch schenken kann?
Jain:

Code: Alles auswählen

yield gates[candidate]

Verfasst: Donnerstag 28. Januar 2010, 23:27
von fledermausland
Oh, Tatsache. KISS lässt grüßen.

Verfasst: Freitag 29. Januar 2010, 04:01
von snafu
Wobei man, wenn der Wahrheitswert noch relevant ist, hier mit `yield candidate in gates` arbeiten sollte.

Verfasst: Freitag 29. Januar 2010, 06:53
von Hyperion
Hui, das hatte ich doch auch mal versucht umzusetzen.
http://www.python-forum.de/topic-13721.html

Was ist das teilweise krude... :-D

Verfasst: Freitag 29. Januar 2010, 09:23
von jbs
snafu hat geschrieben:Wobei man, wenn der Wahrheitswert noch relevant ist, hier mit `yield candidate in gates` arbeiten sollte.
Ich glaube das hilft nicht. gates enthält 0 und 1. candidate kann 0,1 oder 2 sein. candidate ist der index. candidate in gates hilft deshalb nicht.