Duc de Toscane avec Python et Pygal

Le module pygal de Python permet de faire de très jolis diagrammes au format svg visualisables dans son
navigateur libre préféré.
Pour illustrer notre propos, occupons-nous du célèbre problème du duc de Toscane.
On utilisera python3 via ipython en le lançant avec pylab :

Bash
$ ipython --pylab

randint(1,7,size = 3) renvoie une liste de trois entiers aléatoirement choisis entre 1 et 6 = 7-1 selon la loi uniforme discrète (c'est le randint de pylab).
Counter(s) renvoie les effectifs de chaque élément de s.

Python
from collections import Counter
s = [sum( randint(1,7,size = 3) ) for k in range(100000)]
c = Counter(s)

On obtient le dictionnaire des effectifs:

Python
Counter({11: 12651, 10: 12288, 9: 11582, 12: 11455, 8: 9750, 13: 9662, 7: 7134, 14: 6940, 15: 4643, 6: 4638, 16: 2827, 5: 2681, 17: 1460, 4: 1366, 3: 480, 18: 443})

Pour un rendu graphique d'une grande qualité, on utilise le module pygal de Python:

Python
from pygal import *
toscane = HorizontalBar()
for v in c:
    toscane.add(str(v),c[v])
toscane.render_to_file("toscane.svg")
toscane.title = "Effectifs des sorties du problème du duc de Toscane"

Ensuite, depuis ipython on peut visualiser sur firefox par exemple avec la commande:

Bash
!firefox ./toscane.svg

Voici le magnifique rendu SVG : n'hésitez pas à survoler les différentes parties de la page avec votre souris.

Sucre : lancers consécutifs égaux

La fonction groupby de Python (inspirée du groupBy de Haskell) permet de découper une liste en sous-listes uniformes.

Par exemple [1,1,1,1,2,2,3,3,1,1,1,2,2] --> [1,1,1,1],[2,2],[3,3],[1,1,1],[2,2]

En fait, Python renvoie deux itérateurs, l'un énumérant les valeurs, l'autre les sous-listes:

Python
In [45]: [valeur for valeur,groupe in groupby([1,1,1,1,2,2,3,3,1,1,1,2,2])]
Out[45]: [1, 2, 3, 1, 2]
 
In [46]: [list(groupe) for valeur,groupe in groupby([1,1,1,1,2,2,3,3,1,1,1,2,2])]
Out[46]: [[1, 1, 1, 1], [2, 2], [3, 3], [1, 1, 1], [2, 2]]

Cela va nous servir à résoudre le problème suivant: on lance 10 fois de suite une pièce de monnaie et on voudrait observer la moyenne du nombre maximal de résultats consécutifs égaux.

Python
from collections import Counter
from itertools import groupby
 
def plus_gd_paquet(liste):
    return max([len(list(groupe)) for cle, groupe in groupby(liste)])
 
def dic_paquet(n):
    s = [plus_gd_paquet(randint(0,2,size = 10) ) for k in range(n)]
    return  Counter(s)
 
def moyenne_paquet(n):
    c = dic_paquet(n)
    return sum([k * c[k] for k in c]) / n

Alors:

Python
In [50]: dic_paquet(100000)
Out[50]: Counter({3: 36124, 4: 24972, 2: 17065, 5: 12364, 6: 5422, 7: 2304, 8: 988, 9: 404, 10: 179, 1: 178})
 
In [51]: moyenne_paquet(100000)
Out[51]: 3.6552500000000001