RICM4: Probabilité et Simulation. DM1: Pierre Feuille Ciseaux
Table des matières
1 Pierre, Feuille, Ciseau chez les poissons!
1.1 Mise en oeuvre d'une petite simulation
Commençons par une petite fonction générant une série d'actions sans mémoire et suivant une loi de probabilité donnée.
PFC = as.factor(c("Pierre","Feuille","Ciseau")); PFC_sequence = function(N=1000,P=c(1/3,1/3,1/3)) { sample(PFC,N,P,replace=T) } PFC_sequence(20)
[1] Pierre Ciseau Feuille Pierre Feuille Pierre Pierre Feuille Feuille [10] Ciseau Feuille Pierre Feuille Ciseau Pierre Pierre Pierre Ciseau [19] Pierre Ciseau Levels: Ciseau Feuille Pierre
Vérifions que le code est "correct" et que ça n'a pas l'air trop biaisé.
summary(PFC_sequence(1000))
Ciseau Feuille Pierre 330 326 344
Bon, comment coder ces comparaisons Pierre, Feuille, Ciseau "simplement". Je les ai représentées comme des facteurs mais je peux convertir ces facteurs en numéros et à partir de là, utiliser l'ordre sur ces nombres modulo 3.
PFC as.numeric(PFC)
[1] Pierre Feuille Ciseau Levels: Ciseau Feuille Pierre [1] 3 2 1
C'est l'ordre alphabétique qui a été utilisé. On a donc avec ce codage "3 < 2 < 1 < 3". Comme demandé dans l'énoncé, j'utiliserai le point de vue du joueur B pour déterminer les gains:
PFC_outcome = function(A_seq, B_seq) { ## print(A_seq); ## print(B_seq); A_seq = as.numeric(A_seq); B_seq = as.numeric(B_seq); outcome = ((3+A_seq-B_seq)%%3) outcome = ifelse(outcome==2,-1,outcome) ## print(outcome) outcome } A = PFC_sequence(8) B = PFC_sequence(8) A B PFC_outcome(A,B)
[1] Feuille Pierre Pierre Pierre Ciseau Ciseau Feuille Pierre Levels: Ciseau Feuille Pierre [1] Ciseau Feuille Feuille Ciseau Ciseau Feuille Ciseau Feuille Levels: Ciseau Feuille Pierre [1] 1 1 1 -1 0 -1 1 1
Écrivons une petite fonction calculant le gain moyen d'une partie entre deux joueurs sans mémoire:
PFC_memoryless_game = function(N,pA,pB) {
A = PFC_sequence(N,pA)
B = PFC_sequence(N,pB)
mean(PFC_outcome(A,B))
}
1.2 Le joueur biaisé
On suppose que le joueur \(A\) joue avec probabilités \(P^{(A)}=(1/4,1/4,1/2)\).
pA = c(1/4,1/4,1/2) PFC_memoryless_game(1E6,pA,c(1/4,1/4,1/2)) PFC_memoryless_game(1E6,pA,c(1/3,1/3,1/3))
[1] -0.001121 [1] 0.000253
Mmmh, pas fameux. Quand je répète le bloc précédent, ça oscille autour de 0. J'ai choisi un million de tirages pour que ça n'oscille "pas trop". Si on avait vu les cours sur l'intervalle de confiance, on saurait qu'il y a 99.9% de chances pour que ça correspond à une approximation 0.003 près (\(3*1/\sqrt(N)\)), Autrement, dit, les deux premières décimales sont assez fiables. Tu parles d'un jeu inintéressant…
Regardons s'il n'y a pas une stratégie plus intéressante
pA = c(1/4,1/4,1/2) Gain = data.frame(); for(x in ((0:10)/10)) { for(y in ((0:10)/10)) { if(x+y<=1) { gain = PFC_memoryless_game(1000000,pA,c(x,y,1-(x+y))); Gain = rbind(Gain, data.frame(x=x,y=y,gain=gain)); } } } head(Gain); tail(Gain); summary(Gain);
x y gain 1 0 0.0 -0.000015 2 0 0.1 -0.024234 3 0 0.2 -0.050794 4 0 0.3 -0.076173 5 0 0.4 -0.100791 6 0 0.5 -0.124812 x y gain 61 0.8 0.0 0.200174 62 0.8 0.1 0.174111 63 0.8 0.2 0.150387 64 0.9 0.0 0.224514 65 0.9 0.1 0.199667 66 1.0 0.0 0.249588 x y gain Min. :0.0000 Min. :0.0000 Min. :-2.485e-01 1st Qu.:0.1000 1st Qu.:0.1000 1st Qu.:-7.591e-02 Median :0.3000 Median :0.3000 Median :-2.100e-05 Mean :0.3333 Mean :0.3333 Mean : 5.461e-05 3rd Qu.:0.5000 3rd Qu.:0.5000 3rd Qu.: 7.578e-02 Max. :1.0000 Max. :1.0000 Max. : 2.496e-01
Le max est à 0.2496. Vers où se trouve-t-il ?
head(Gain[order(-Gain$gain),])
x y gain 66 1.0 0.0 0.249588 64 0.9 0.0 0.224514 61 0.8 0.0 0.200174 65 0.9 0.1 0.199667 57 0.7 0.0 0.174610 62 0.8 0.1 0.174111
C'est à peu près clair, il faut jouer systématiquement là où ça fait mal. Il joue souvent "Ciseau", il faut jouer "Pierre" systématiquement pour écraser \(A\). Regardons si d'autres stratégies sont bonnes:
library(ggplot2) ggplot(Gain, aes(x=x,y=y,fill=gain)) + geom_tile() + scale_fill_gradient2( low = "red", high = "blue", mid = "white")
C'est super linéaire comme comportement. Il doit y avoir une formule toute simple. :)
On a le tableau suivant:
\(A \backslash B\) | P \((x)\) | F \((y)\) | C \((1-(x+y))\) |
---|---|---|---|
P \((1/4)\) | 0 | 1 | -1 |
F \((1/4)\) | -1 | 0 | 1 |
C \((1/2)\) | 1 | -1 | 0 |
L'espérance du gain de \(B\) est donc \(1/4.(y - (1-(x+y))) + 1/4.(-x + (1-(x+y))) + 1/2(x-y) = (x-y)/4\). C'est bien maximum pour \((x,y)=(1,0)\)!
1.3 Le joueur non biaisé
Supposons maintenant que le joueur \(A\) est non biaisé, i.e., que \(P^{(A)}=(1/3,1/3,1/3)\).
pA = c(1/3,1/3,1/3) Gain = data.frame(); for(x in ((0:10)/10)) { for(y in ((0:10)/10)) { if(x+y<=1) { gain = PFC_memoryless_game(1000000,pA,c(x,y,1-(x+y))); Gain = rbind(Gain, data.frame(x=x,y=y,gain=gain)); } } } summary(Gain)
x y gain Min. :0.0000 Min. :0.0000 Min. :-1.905e-03 1st Qu.:0.1000 1st Qu.:0.1000 1st Qu.:-7.897e-04 Median :0.3000 Median :0.3000 Median :-1.130e-04 Mean :0.3333 Mean :0.3333 Mean :-9.118e-05 3rd Qu.:0.5000 3rd Qu.:0.5000 3rd Qu.: 5.462e-04 Max. :1.0000 Max. :1.0000 Max. : 2.154e-03
Les gains pour \(B\) sont partout proche de 0. On dirait que \(A\) ne peut pas perdre! Ceci dit, il ne peut pas gagner non plus. Regardons, si on voit une structure ou pas:
library(ggplot2) ggplot(Gain, aes(x=x,y=y,fill=gain)) + geom_tile() + scale_fill_gradient2( low = "red", high = "blue", mid = "white")
Et non, ça a l'air complètement bruité et proche de 0. Si on reprend le calcul précédent, on verra vite que tout se simplifie et que l'espérance du gain est bien nulle.
2 Apprentissage
Notre objectif est maintenant de créer un programme qui arrive à gagner contre un joueur humain. Nous savons que les humains jouent mal au hasard et peuvent en particulier avoir du mal à maintenir l'uniformité.
2.1 Un algorithme qui apprend et joue optimalement
Si notre adversaire est biaisé et n'arrive pas à maintenir l'uniformité, il suffit de jouer systématiquement contre ce qu'il joue le plus fréquemment.
PFC_Strat1 = function(Aseq) { history = PFC; # Let's assume A played both three values before at least once Bseq = rep(PFC[1],length(Aseq)); for(i in (1:length(Aseq))) { ## print("*******") ## print(history); stat = c(length(history[history=="Pierre"]), length(history[history=="Feuille"]), length(history[history=="Ciseau"])) maxplayed = head(PFC[stat == max(stat)],n=1) ## print(maxplayed) Bseq[i] = PFC[as.numeric(PFC)==(((as.numeric(maxplayed)+1)%%3)+1)] ## print("-----") ## print(Bseq); history[length(history)+1] = Aseq[i]; } Bseq }
Regardons s'il gagne contre un joueur biaisé:
pA = c(1/4,1/4,1/2) A_seq = PFC_sequence(200,pA) B_seq = PFC_Strat1(A_seq) A_seq B_seq mean(PFC_outcome(A_seq,B_seq))
[1] Ciseau Feuille Pierre Pierre Feuille Ciseau Ciseau Ciseau Ciseau [10] Pierre Pierre Ciseau Ciseau Pierre Pierre Ciseau Ciseau Pierre [19] Pierre Feuille Ciseau Ciseau Ciseau Pierre Ciseau Ciseau Ciseau [28] Ciseau Ciseau Ciseau Ciseau Ciseau Ciseau Feuille Ciseau Ciseau [37] Pierre Pierre Ciseau Feuille Ciseau Ciseau Ciseau Pierre Pierre [46] Pierre Pierre Feuille Ciseau Feuille Pierre Ciseau Ciseau Ciseau [55] Ciseau Pierre Pierre Ciseau Ciseau Pierre Pierre Ciseau Ciseau [64] Feuille Pierre Pierre Pierre Ciseau Ciseau Ciseau Ciseau Feuille [73] Pierre Ciseau Ciseau Ciseau Pierre Pierre Ciseau Pierre Ciseau [82] Feuille Ciseau Pierre Ciseau Feuille Pierre Feuille Ciseau Ciseau [91] Pierre Pierre Ciseau Ciseau Ciseau Ciseau Ciseau Pierre Feuille [100] Ciseau Pierre Ciseau Ciseau Feuille Ciseau Pierre Ciseau Ciseau [109] Feuille Pierre Pierre Ciseau Ciseau Pierre Feuille Ciseau Feuille [118] Ciseau Feuille Feuille Ciseau Ciseau Ciseau Ciseau Pierre Ciseau [127] Ciseau Ciseau Feuille Feuille Feuille Pierre Ciseau Feuille Ciseau [136] Ciseau Pierre Pierre Pierre Feuille Feuille Feuille Feuille Ciseau [145] Pierre Ciseau Pierre Ciseau Feuille Feuille Pierre Pierre Feuille [154] Ciseau Ciseau Feuille Ciseau Ciseau Ciseau Feuille Ciseau Feuille [163] Ciseau Ciseau Ciseau Ciseau Ciseau Ciseau Pierre Feuille Feuille [172] Ciseau Pierre Feuille Feuille Ciseau Pierre Feuille Ciseau Ciseau [181] Feuille Pierre Ciseau Ciseau Ciseau Feuille Ciseau Ciseau Feuille [190] Ciseau Ciseau Ciseau Pierre Pierre Feuille Ciseau Ciseau Pierre [199] Ciseau Ciseau Levels: Ciseau Feuille Pierre [1] Feuille Pierre Ciseau Feuille Feuille Feuille Feuille Pierre Pierre [10] Pierre Pierre Pierre Pierre Pierre Pierre Pierre Pierre Pierre [19] Pierre Pierre Pierre Pierre Pierre Pierre Pierre Pierre Pierre [28] Pierre Pierre Pierre Pierre Pierre Pierre Pierre Pierre Pierre [37] Pierre Pierre Pierre Pierre Pierre Pierre Pierre Pierre Pierre [46] Pierre Pierre Pierre Pierre Pierre Pierre Pierre Pierre Pierre [55] Pierre Pierre Pierre Pierre Pierre Pierre Pierre Pierre Pierre [64] Pierre Pierre Pierre Pierre Pierre Pierre Pierre Pierre Pierre [73] Pierre Pierre Pierre Pierre Pierre Pierre Pierre Pierre Pierre [82] Pierre Pierre Pierre Pierre Pierre Pierre Pierre Pierre Pierre [91] Pierre Pierre Pierre Pierre Pierre Pierre Pierre Pierre Pierre [100] Pierre Pierre Pierre Pierre Pierre Pierre Pierre Pierre Pierre [109] Pierre Pierre Pierre Pierre Pierre Pierre Pierre Pierre Pierre [118] Pierre Pierre Pierre Pierre Pierre Pierre Pierre Pierre Pierre [127] Pierre Pierre Pierre Pierre Pierre Pierre Pierre Pierre Pierre [136] Pierre Pierre Pierre Pierre Pierre Pierre Pierre Pierre Pierre [145] Pierre Pierre Pierre Pierre Pierre Pierre Pierre Pierre Pierre [154] Pierre Pierre Pierre Pierre Pierre Pierre Pierre Pierre Pierre [163] Pierre Pierre Pierre Pierre Pierre Pierre Pierre Pierre Pierre [172] Pierre Pierre Pierre Pierre Pierre Pierre Pierre Pierre Pierre [181] Pierre Pierre Pierre Pierre Pierre Pierre Pierre Pierre Pierre [190] Pierre Pierre Pierre Pierre Pierre Pierre Pierre Pierre Pierre [199] Pierre Pierre Levels: Ciseau Feuille Pierre [1] 0.29
Alors évidemment, cet algorithme joue optimalement mais un humain pas trop bête devrait vite comprendre cette stratégie et arriver à rééquilibrer, par exemple en jouant systématiquement contre ce que l'ordinateur vient de jouer…
Pour que cette stratégie soit un peu moins grossièrement détectable, jouons un peu au hasard quand même (avec une proba 1/2 par exemple).
PFC_Strat2 = function(Aseq, proba = 1/2) { history = PFC; # Let's assume A played both three values before at least once Bseq = rep(PFC[1],length(Aseq)); for(i in (1:length(Aseq))) { if(runif(1)<proba) { stat = c(length(history[history=="Pierre"]), length(history[history=="Feuille"]), length(history[history=="Ciseau"])) maxplayed = head(PFC[stat == max(stat)],n=1) Bseq[i] = PFC[as.numeric(PFC)==(((as.numeric(maxplayed)+1)%%3)+1)] } else { Bseq[i] = sample(PFC,1) } history[length(history)+1] = Aseq[i]; } Bseq }
Regardons s'il gagne contre un joueur biaisé:
pA = c(1/4,1/4,1/2) A_seq = PFC_sequence(200,pA) B_seq = PFC_Strat2(A_seq) A_seq B_seq mean(PFC_outcome(A_seq,B_seq))
[1] Pierre Pierre Ciseau Feuille Pierre Feuille Ciseau Feuille Feuille [10] Feuille Feuille Ciseau Ciseau Feuille Ciseau Ciseau Ciseau Feuille [19] Ciseau Ciseau Pierre Ciseau Pierre Ciseau Feuille Ciseau Pierre [28] Feuille Pierre Pierre Ciseau Pierre Feuille Feuille Ciseau Ciseau [37] Ciseau Pierre Pierre Ciseau Ciseau Ciseau Pierre Ciseau Feuille [46] Ciseau Feuille Ciseau Ciseau Pierre Ciseau Feuille Ciseau Feuille [55] Feuille Pierre Pierre Feuille Feuille Pierre Feuille Ciseau Feuille [64] Ciseau Feuille Feuille Ciseau Ciseau Ciseau Ciseau Ciseau Ciseau [73] Pierre Pierre Feuille Pierre Pierre Feuille Pierre Pierre Feuille [82] Ciseau Pierre Pierre Pierre Ciseau Feuille Ciseau Ciseau Feuille [91] Ciseau Feuille Ciseau Feuille Pierre Feuille Pierre Feuille Pierre [100] Feuille Feuille Pierre Ciseau Ciseau Pierre Pierre Ciseau Ciseau [109] Pierre Pierre Ciseau Ciseau Ciseau Ciseau Pierre Ciseau Ciseau [118] Ciseau Ciseau Feuille Ciseau Ciseau Ciseau Ciseau Feuille Pierre [127] Ciseau Ciseau Pierre Ciseau Ciseau Pierre Ciseau Ciseau Pierre [136] Ciseau Ciseau Ciseau Feuille Feuille Ciseau Ciseau Feuille Ciseau [145] Pierre Feuille Ciseau Ciseau Pierre Feuille Pierre Ciseau Ciseau [154] Ciseau Pierre Feuille Pierre Pierre Feuille Ciseau Pierre Ciseau [163] Feuille Ciseau Feuille Pierre Feuille Pierre Pierre Pierre Ciseau [172] Ciseau Pierre Pierre Pierre Feuille Feuille Ciseau Pierre Ciseau [181] Pierre Pierre Ciseau Feuille Feuille Ciseau Ciseau Ciseau Ciseau [190] Ciseau Ciseau Ciseau Pierre Ciseau Feuille Pierre Pierre Feuille [199] Ciseau Ciseau Levels: Ciseau Feuille Pierre [1] Feuille Ciseau Feuille Pierre Feuille Pierre Feuille Ciseau Feuille [10] Ciseau Ciseau Ciseau Ciseau Ciseau Ciseau Ciseau Feuille Ciseau [19] Ciseau Ciseau Ciseau Pierre Feuille Feuille Feuille Pierre Pierre [28] Pierre Feuille Ciseau Ciseau Pierre Feuille Pierre Ciseau Pierre [37] Pierre Feuille Pierre Feuille Pierre Pierre Pierre Pierre Pierre [46] Pierre Feuille Pierre Pierre Pierre Ciseau Pierre Pierre Pierre [55] Feuille Feuille Feuille Pierre Pierre Ciseau Pierre Pierre Pierre [64] Pierre Pierre Pierre Pierre Pierre Pierre Pierre Pierre Feuille [73] Feuille Pierre Pierre Pierre Ciseau Pierre Pierre Pierre Pierre [82] Feuille Pierre Ciseau Pierre Pierre Pierre Pierre Pierre Pierre [91] Ciseau Pierre Pierre Feuille Feuille Pierre Pierre Feuille Feuille [100] Pierre Ciseau Pierre Ciseau Ciseau Feuille Pierre Ciseau Feuille [109] Ciseau Pierre Pierre Ciseau Pierre Pierre Pierre Pierre Pierre [118] Feuille Pierre Ciseau Pierre Pierre Feuille Ciseau Pierre Pierre [127] Feuille Ciseau Pierre Pierre Feuille Pierre Pierre Feuille Pierre [136] Pierre Pierre Feuille Ciseau Feuille Feuille Pierre Pierre Ciseau [145] Pierre Pierre Feuille Pierre Feuille Ciseau Pierre Pierre Pierre [154] Pierre Feuille Pierre Ciseau Feuille Feuille Pierre Pierre Pierre [163] Pierre Pierre Pierre Pierre Pierre Pierre Pierre Pierre Feuille [172] Pierre Pierre Ciseau Pierre Ciseau Feuille Ciseau Feuille Pierre [181] Pierre Pierre Pierre Pierre Feuille Pierre Pierre Feuille Feuille [190] Pierre Pierre Pierre Pierre Pierre Pierre Pierre Feuille Pierre [199] Ciseau Pierre Levels: Ciseau Feuille Pierre [1] 0.095
Bon, on gagne mais 1/2, ça reste un peu trop visible, mais il faut savoir gagner peu pour gagner. Avec .1, on n'y verra que du feu! :) Quoi qu'il en soit, on a un compromis.
pA = c(1/4,1/4,1/2) A_seq = PFC_sequence(2000,pA) B_seq = PFC_Strat2(A_seq, proba=.1) mean(PFC_outcome(A_seq,B_seq))
[1] 0.0545
3 Bonus au choix
3.1 Extension spéciale Big Bang Theory
Adapter votre programme et votre étude au jeu Pierre-Papier-Ciseaux-Lézard-Spock.
Le plus dur est d'arriver à trouver un codage des facteurs avec lequel les calculs de modulo marchent bien.
3.2 Proposez une stratégie non aléatoire
Je vous invite à ce document (un peu long, mais rigolo) sur le sujet: http://apiacoa.org/publications/2004/rossi2004opponent.pdf