Vannak társasjátékok, ahol nem egy, hanem két vagy több dobókockát kell használni. Ilyen például a Catan telepesei nevű népszerű stratégiai táblás játék. Itt a játéktér hexagonális mezőihez, amelyek bizonyos nyersanyagokat termelnek, a játék indulásakor 2-től 12-ig terjedő számok rendelődnek.

Két dobókockával kell dobni, és a dobás után a kockák pontértékeinek összegének megfelelő számmal rendelkező mezők termelnek csak nyersanyagot a mezők tulajdonosai számára.
Bár az egyes kockákkal való dobás egyenletes eloszlású, vagyis nagyszámú dobás esetén minden szám relatív gyakorisága tart az elméleti 1/6 ≈ 16.67% valószínűséghez, a két kocka véletlen értékeinek összege már nem egyenletes eloszlású. Ezt a táblán a mezőkre helyezett számkorongok mérete is jelzi: minél nagyobb a korong, annál nagyobb e szám valószínűsége.
A kérdés az: milyen eloszlású a két kocka dobásakor kiadódó értékek összege?
A valószínűségszámításban jártas játékosok persze tudják, hogy két független, egyenletes eloszlású valószínűségi változó összege szimmetrikus háromszögeloszlású lesz. A kevésbé jártasak azonban, némi programozási ismeret birtokában, lemodellezhetik ezt a helyzetet és a kapott eredmények alapján ugyanerre az eredményére juthatnak.
Ezt tesszük most mi is, és az alábbi programmal vizsgáljuk a két kocka dobásakor kiadódó gyakoriságértékeket.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
from random import randint from collections import Counter import matplotlib.pyplot as plt def visualize(data, xlabel='', ylabel='', title=''): """A data adasor histogramját jeleníti meg.""" plt.hist(data, bins=30, color='skyblue', edgecolor='black') plt.xlabel(xlabel); plt.ylabel(ylabel); plt.title(title) plt.show() # A kockadobások száma. number_of_rolls = 1000000 # A két dobókockával történő dobások kimeneteleinek (a kockákon megjelenő értékek) előállítása # két, egyenletes eloszlású, 1..6 egész értékeket kiadó véletlenszámgenerátorral. two_dice_rolls_outcomes = [(randint(1, 6), randint(1, 6)) for _ in range(number_of_rolls)] # Meghatározzuk a kimenetelek egyes kockaértékeinek gyakoriságát annak ellenőrzéséhez, hogy # az egyes kockákon a dobások után megjelenő számok eloszlása egyenletes. dice1_nums_freq = Counter([d[0] for d in two_dice_rolls_outcomes]) dice2_nums_freq = Counter([d[1] for d in two_dice_rolls_outcomes]) # Előállítjuk a dobókockák kimeneteleinek relatív gyakoriságait. # Elméletileg mind a hat szám 1/6 = 16,67% valószínűséggel kell, hogy előforduljon. rel_freq1 = {n: '{:.2%}'.format(freq / number_of_rolls) for n, freq in dice1_nums_freq.items()} rel_freq2 = {n: '{:.2%}'.format(freq / number_of_rolls) for n, freq in dice2_nums_freq.items()} # Előállítjuk az egyes kimenetelekre a két kockaérték összegét. two_dice_sum = [sum(db) for db in two_dice_rolls_outcomes] # Meghatározzuk az értékösszegek gyakoriságát. sum_freq = Counter(two_dice_sum) # Annak igazolása, hogy a két kockával dobás összegének gyakorisága szimmetrikus háromszög eloszlású. # A legkisebb valószínűségű esetek, amikor az összeg 2 vagy 12. Ezek elméleti valószínűsége: p2_12 = 1 / 36 # = 0.0278 # Előállítjuk a kockaértékek összegének gyakoriságát a legkisebb valószínűségű esetek gyakoriságához viszonyítva. sum_freq_dev = Counter({n: '{:.2}'.format(freq / (p2_12 * number_of_rolls)) for n, freq in sum_freq.items()}) |
A fenti programban először eltároljuk sok dobás kimeneteleit. Aztán meggyőződünk arról, hogy az egyes dobókockák által produkált számokat modellező (ál)véletlen számok egyenletes előoszlásúak. Ezt követően a tárolt dobáskimenetelek alapján képezzük a kockaértékek összegének sorozatát, majd pedig előállítjuk az összeg-gyakoriság párokat.
Azt pedig, hogy ezek szimmetrikus háromszögeloszlást követnek úgy ellenőrizzük, hogy előállítjuk a kockaértékek összegének gyakoriságát a legkisebb valószínűségű esetek (2 és 12 értékek) gyakoriságához viszonyítva. És ha igaz az állítás, akkor az így kapott relatív gyakoriságértékek a legkisebb gyakoriságú értéknek egymás követő egész számú többszörösei.
Alább láthatjuk az eredményeket megjelenítő kódokat és a kiírt gyakoriságértékeket, valamint azok hisztogramját. Ebből és a legkisebb valószínűségű esetek gyakoriságához viszonyított értékekből is látható az eloszlás szimmetrikus háromszög jellege.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
# Eredények megjelenítése. print('Egyik kocka dobásértékeinek relatív gyakorisága:', rel_freq1) print('Másik kocka dobásértékeinek relatív gyakorisága:', rel_freq2) # A kockaértékek összegének gyakoriságát kiírjuk és hisztogramon megjelenítjük. print('Két kockával dobás összegének gyakorisága:\n', dict(sum_freq.most_common())) visualize(two_dice_sum, 'Dobások összege', 'Gyakoriság', 'Két kockával dobás összegének eloszlása') print('Két kockával dobás összegének gyakoriságai a legkisebb gyakoriságú értékhez viszonyítva:\n', dict(sum_freq_dev.most_common())) # Eredmények: # Egyik kocka dobásértékeinek relatív gyakorisága: # {2: '16.74%', 4: '16.68%', 6: '16.65%', 5: '16.63%', 1: '16.62%', 3: '16.68%'} # Másik kocka dobásértékeinek relatív gyakorisága: # {3: '16.67%', 5: '16.60%', 2: '16.71%', 1: '16.65%', 6: '16.69%', 4: '16.69%'} # Két kockával dobás összegeinek gyakoriságai: # {7: 167009, 6: 139526, 8: 138798, 9: 111616, 5: 111009, 4: 83338, 10: 82840, 3: 55455, 11: 55079, 12: 27736, 2: 27594} # Két kockával dobás összegeinek gyakoriságai a legkisebb gyakoriságú értékhez viszonyítva: # {7: '6.0', 8: '5.0', 6: '5.0', 5: '4.0', 9: '4.0', 10: '3.0', 4: '3.0', 3: '2.0', 11: '2.0', 12: '1.0', 2: '0.99'} |

Mi lenne akkor, hogy a játékot úgy módosítanánk, hogy kettő helyett három kockával dobunk? Ilyenkor persze a mezők száma is meg kell, hogy nőjön, hiszen most az azokra helyezhető számkorongok 3-tól 18-ig számozódnak. De most ez nem számít, mert csak arra vagyunk kíváncsiak, hogy milyen eloszlású lenne három kocka dobásakor kiadódó értékek összege.
Most is követhetjük a két kocka esetén alkalmazott megközelítést természetesen az értelemszerű módosításokkal. Az így adódó programsorokat és azok futtatási eredményeit követhetjük alább.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
# Három kockával dobás. # A három dobókockával történő dobások kimeneteleinek előállítása # három, egyenletes eloszlású, 1..6 egész értékeket kiadó véletlenszámgenerátorral. three_dice_rolls_outcomes = [(randint(1, 6), randint(1, 6), randint(1, 6)) for _ in range(number_of_rolls)] # Előállítjuk az egyes kimenetelekre a két kockaérték összegét. three_dice_sum = [sum(db) for db in three_dice_rolls_outcomes] # Meghatározzuk az értékösszegek relatív gyakoriságát. sum_rel_freq = Counter({n: freq / number_of_rolls for n, freq in Counter(three_dice_sum).items()}) # Eredények megjelenítése. # A kockaértékek összegének relatív gyakoriságát kiírjuk. print('Három kockával dobás összegének relatív gyakorisága:\n', {n: f'{fr:.1%}' for n, fr in sum_rel_freq.most_common()}) # A kockaértékek összegének gyakoriságát hisztogramon megjelenítjük. visualize(three_dice_sum, 'Dobások összege', 'Gyakoriság', 'Három kockával dobás összegének eloszlása') # Eredmények: # Három kockával dobás összegének relatív gyakorisága: # {10: '12.5%', 11: '12.5%', 12: '11.6%', 9: '11.6%', # 8: '9.7%', 13: '9.7%', 14: '7.0%', 7: '6.9%', # 6: '4.6%', 15: '4.6%', 5: '2.8%', 16: '2.8%', # 17: '1.4%', 4: '1.4%', 18: '0.5%', 3: '0.5%'} |

A kiírt gyakoriság értékekből, de különösen a megjelenített eloszlás ábrájából jól látható, hogy nem háromszög eloszlást kapunk. Kicsit jobban megvizsgálva az ábrát és a számszerű eredményeket az is látszik, hogy egy ilyen módosított játékban nem lenne igazán érdemes az eloszlás széleihez közel eső számértékű mezőkért küzdeni, mert gyakoriságuk sokkal kisebb, mint az eloszlás közepéhez közel eső számoké. Más szóval, változatlan koncepció és alapszabályok esetén, játékélmény tekintetében nem sok értelme lenne a három kockával való dobásnak és a megnövelt számú mezőnek.
Álvéletlen számok előállításával a Python tudásépítés lépésről lépésre című e-könyvben a „Készétel fogyasztás – a szabványos könyvtár moduljainak használata” fejezeten belül a „A véletlen használatba vétele” című alfejezet foglalkozik. A gyakoriságértékek meghatározására alkalmazott Counter objektum használatáról és egyéb alkalmazási lehetőségeiről a „Speciális konténer típusok” alfejezet „Sorozatelemek összeszámolása – Counter” címe alatt olvashatunk.