Python-code bij "Dobbelspel"

Flippo

Dit programma gaat gegeven $4$ getallen $a, b, c$ en $d$ na of je hiermee $N$ kunt verkrijgen m.b.v. de bewerkingen ${}+{}, {}-{}, {}\times{}$ en ${}\div{}$.

Er ligt een spelvoorstel. Flippo wordt in een groep gespeeld met vier dobbelstenen en een zak met de getallen $1$ t/m $100$. Als iemand aan de beurt is gooit hij met de vier dobbelstenen en trekt hij een getal. De speler probeert met behulp van de getallen op de vier dobbelstenen en de vier bewerkingen het getrokken getal te verkrijgen.

We rekenen uit wat de kans is om dit getrokken getal te verkrijgen. Daartoe bekijken we alle mogelijkheden $6^4 \cdot 100=129600$ en tellen voor elk van deze mogelijkheden of de mogelijkheid leidt tot succes.

De volgende vraag is of het uitmaakt of je met gewone dobbelstenen gooit of met dobbelstenen met andere getallen op de zijvlakken. Het wijzigen van de getallen op de zijvlakken van de dobbelstenen vergroot de kans op succes aanzienlijk.

De kans op succes met gewonen dobbelstenen is $41{,}8\%$, maar als je in plaats van deze dobbelstenen de volgende vier dobbelstenen kiest:

  • $(1, 2, 3, 4, 5, 6)$
  • $(2, 4, 6, 8, 10, 12)$
  • $(3, 6, 9, 12, 15, 18)$
  • $(4, 8, 12 16, 20, 24)$ vergroot je de kans al tot $57{,}5\%$. Ik heb rond de $10$ experimenten gedaan. De beste $4$ dobbelstenen die ik heb gevonden zijn:
  • $(1, 2, 3, 4, 5, 6)$
  • $(2, 4, 6, 8, 10, 12)$
  • $(2, 3, 5, 7, 11, 13)$
  • $(3, 6, 9, 12, 15, 18)$ met een kans op succes van $60{,}44\%$.

Er kan op twee manieren worden gewerkt aan dit project:

  1. Zoek de optimale dobbelstenen. Mogelijk maakt het al uit als op één dobbelsteen een getal iets afwijkt. Minimale vereiste: alle getallen op elk van de dobbelstenen zijn verschillend, positief en geheel. Het is geen probleem als op verschillende dobbelstenen hetzelfde getal voorkomen.
  2. Pas de code aan zodat die sneller draait. Momenteel doet de code er rond de $11$ minuten over, maar dat kan aanzienlijk worden versneld, door doublures in de berekeningen te verwijderen. Er wordt bijvoorbeeld berekend  $((a+b)+c)+d$, $(a+(b+(c+d))$ en $(a+b)+(c+d)$, zonder dat al die verschillende berekeningen nodig zijn. Alle permutaties van $a,b,c$ en $d$worden altijd getest, ook als twee van de getallen gelijk zijn. Lukt het om de code altijd binnen de $10$ minuten te laten draaien, of misschien nog sneller?
# Versie van Flippo die de oplossing ook weergeeft
import time
import math
from itertools import permutations 

def vereenvoudig(u):
    if u[1] < 0:
        u[0] = -u[0]
        u[1] = -u[1]
    g = math.gcd(u[0],u[1])
    if g < 0:
        g = -g
    if g == 0:
        return ([0,0])
    else:
        return ([u[0]//g,u[1]//g])

def fplus(u,v):
    return vereenvoudig([u[0]*v[1]+u[1]*v[0],u[1]*v[1]])

def fmin(u,v):
    return vereenvoudig([u[0]*v[1]-u[1]*v[0],u[1]*v[1]])

def fmaal(u,v):
    return vereenvoudig([u[0]*v[0],u[1]*v[1]])

def fdeel(u,v):
    return vereenvoudig([u[0]*v[1],u[1]*v[0]])

def fop(u,v,op):
    if op == 0:
        return fplus(u,v)
    if op == 1:
        return fmin(u,v)
    if op == 2:
        return fmaal(u,v)
    if op == 3:
        return fdeel(u,v)
    
t0 = time.process_time()
  
Q = [3,3,11,25,24]
OPS = ["+", "-", "x", ":"]
a = Q[0]
b = Q[1]
c = Q[2]
d = Q[3]
r = Q[4]
perms = permutations([a,b,c,d]) 
# disc = (a-b)*(a-c)*(a-d)*(b-c)*(b-d)*(c-d)
lipe = list(perms)
lipe.sort()
print(len(lipe))
print(lipe)
for elt in lipe:
    for u1 in range(4):
        r1 = fop([elt[0],1],[elt[1],1],u1)
        for u2 in range(4):
            r2a = fop(r1,[elt[2],1],u2)
            r2b = fop([elt[2],1],r1,u2)
            r2c = fop([elt[2],1],[elt[3],1],u2)
            for u3 in range(4):
                r3a = fop(r2a,[elt[3],1],u3)
                r3b = fop(r2b,[elt[3],1],u3)
                r3c = fop([elt[3],1],r2a,u3)
                r3d = fop([elt[3],1],r2b,u3)
                r3e = fop(r1,r2c,u3)
                if (r3a[0] == r) & (r3a[1] == 1):
                    #print(r1, r2a, r3a, "(", "(", elt[0], OPS[u1], elt[1], ")", OPS[u2], elt[2], ")", OPS[u3], elt[3])
                    print("(", "(", elt[0], OPS[u1], elt[1], ")", OPS[u2], elt[2], ")", OPS[u3], elt[3])
                if (r3b[0] == r) & (r3b[1] == 1):
                    #print(r1, r2b, r3b, "(", elt[2], OPS[u2], "(", elt[0], OPS[u1], elt[1], ")", ")", OPS[u3], elt[3])
                    print("(", elt[2], OPS[u2], "(", elt[0], OPS[u1], elt[1], ")", ")", OPS[u3], elt[3])
                if (r3c[0] == r) & (r3c[1] == 1):
                    #print(r1, r2a, r3c, elt[3], OPS[u3], "(", "(", elt[0], OPS[u1], elt[1], ")", OPS[u2], elt[2], ")")
                    print(elt[3], OPS[u3], "(", "(", elt[0], OPS[u1], elt[1], ")", OPS[u2], elt[2], ")")
                if (r3d[0] == r) & (r3d[1] == 1):
                    #print(r1, r2b, r3d, elt[3], OPS[u3], "(", elt[2], OPS[u2], "(", elt[0], OPS[u1], elt[1], ")", ")")
                    print(elt[3], OPS[u3], "(", elt[2], OPS[u2], "(", elt[0], OPS[u1], elt[1], ")", ")")
                if (r3e[0] == r) & (r3e[1] == 1):
                    #print(r1, r2c, r3e, "(", elt[0], OPS[u1], elt[1], ")", OPS[u3], "(", elt[2], OPS[u2], elt[3], ")")
                    print("(", elt[0], OPS[u1], elt[1], ")", OPS[u3], "(", elt[2], OPS[u2], elt[3], ")")
t1 = time.process_time()
print("Aantal secondes rekentijd", t1-t0)

Het hoofdprogramma

Hieronder staat het programma dat je kunt gebruiken. In principe hoef je alleen in de set Dobbelsteen1, Dobbelsteen2, Dobbelsteen3, Dobbelsteen4 steeds $6$ verschillende getallen te plaatsen en een run uit te voeren.

De regel met ...% gedaan kun je verwijderen. Je kunt je resultaten in een aparte cell bijhouden. Stuur uiteindelijk je beste resultaat naar [email protected].

import time
import math
from itertools import permutations 

def vereenvoudig(u):
    if u[1] < 0:
        u[0] = -u[0]
        u[1] = -u[1]
    g = math.gcd(u[0],u[1])
    if g == 0:
        return ([0,0])
    else:
        return ([u[0]//g,u[1]//g])

def fplus(u,v):
    return vereenvoudig([u[0]*v[1]+u[1]*v[0],u[1]*v[1]])

def fmin(u,v):
    return vereenvoudig([u[0]*v[1]-u[1]*v[0],u[1]*v[1]])

def fmaal(u,v):
    return vereenvoudig([u[0]*v[0],u[1]*v[1]])

def fdeel(u,v):
    return vereenvoudig([u[0]*v[1],u[1]*v[0]])

def fop(u,v,op):
    if op == 0:
        return fplus(u,v)
    if op == 1:
        return fmin(u,v)
    if op == 2:
        return fmaal(u,v)
    if op == 3:
        return fdeel(u,v)

def flippo(Q):
    a = Q[0]
    b = Q[1]
    c = Q[2]
    d = Q[3]
    r = Q[4]
    perms = permutations([a,b,c,d]) 
    # disc = (a-b)*(a-c)*(a-d)*(b-c)*(b-d)*(c-d)
    lipe = list(perms)
    lipe.sort()
    ok = 0
    for elt in lipe:
        if ok == 0:
            for u1 in range(4):
                r1 = fop([elt[0],1],[elt[1],1],u1)
                for u2 in range(4):
                    r2a = fop([elt[2],1],r1,u2)
                    r2b = fop(r1,[elt[2],1],u2)
                    r2c = fop([elt[2],1],[elt[3],1],u2)
                    for u3 in range(4):
                        r3a = fop([elt[3],1],r2a,u3)
                        r3b = fop([elt[3],1],r2b,u3)
                        r3c = fop(r2a,[elt[3],1],u3)
                        r3d = fop(r2b,[elt[3],1],u3)
                        r3e = fop(r1,r2c,u3)
                        if (r3a[0] == r) & (r3a[1] == 1):
                            ok += 1
                        if (r3b[0] == r) & (r3b[1] == 1):
                            ok += 1
                        if (r3c[0] == r) & (r3c[1] == 1):
                            ok += 1
                        if (r3d[0] == r) & (r3d[1] == 1):
                            ok += 1
                        if (r3e[0] == r) & (r3e[1] == 1):
                            ok += 1
    return ok

Dobbelsteen1 = (1, 2, 3, 4, 5, 6)
Dobbelsteen2 = (1, 2, 3, 4, 5, 6)
Dobbelsteen3 = (1, 2, 3, 4, 5, 6)
Dobbelsteen4 = (1, 2, 3, 4, 5, 6)

Dobbelsteen1 = (1, 2, 3, 4, 5, 6)
Dobbelsteen2 = (7, 8, 9, 10, 11, 12)
Dobbelsteen3 = (13, 14, 15, 16, 17, 18)
Dobbelsteen4 = (19, 20, 21, 22, 23, 24)

Dobbelsteen1 = (1, 2, 3, 4, 5, 6)
Dobbelsteen2 = (2, 4, 6, 8, 10, 12)
Dobbelsteen3 = (3, 6, 9, 12, 15, 18)
Dobbelsteen4 = (4, 8, 12, 16, 20, 24)

print(Dobbelsteen1, Dobbelsteen2, Dobbelsteen3, Dobbelsteen4, ":")

t0 = time.process_time()
niet_geslaagd = 0
for n in range(1,101):
    if n % 10 == 0: print (n, "% gedaan")
    for a1 in Dobbelsteen1:
        for a2 in Dobbelsteen2:
            for a3 in Dobbelsteen3:
                for a4 in Dobbelsteen4:
                    Q = [a1,a2,a3,a4,n]
                    f = flippo(Q)
                    if f == 0:
                        niet_geslaagd +=1
t1 = time.process_time()
print(round(100-niet_geslaagd/1296,2),"% door", \
      Dobbelsteen1, Dobbelsteen2, Dobbelsteen3, Dobbelsteen4)
T = t1-t0
M = math.floor(T / 60)
print("Rekentijd:", M, "minuten", T - 60*M, "seconden")

Ruwe resultaten:

  • $57{,}5\%$ door $(1, 2, 3, 4, 5, 6) (2, 4, 6, 8, 10, 12) (3, 6, 9, 12, 15, 18) (4, 8, 12, 16, 20, 24)$
  • $45{,}12\%$ door $(1, 2, 3, 4, 5, 6) (7, 8, 9, 10, 11, 12) (13, 14, 15, 16, 17, 18) (19, 20, 21, 22, 23, 24)$
  • $41{.}8\%$ door $(1, 2, 3, 4, 5, 6) (1, 2, 3, 4, 5, 6) (1, 2, 3, 4, 5, 6) (1, 2, 3, 4, 5, 6)$