Problem
An ordinary die is rolled until the running total of the rolls first exceeds \(12\). What is the most likely final total that will be obtained?
Solution 1
The first solution approach is quite straightforward. We just use Monte Carlo simulation to estimate the required probability. From the graph below we can see that the final total is most likely to be \(13\) with a probability of \(\approx 28\%\).
import numpy as np
import matplotlib.pyplot as plt
def monte_carlo_simulation(n=100000):
= []
results for _ in range(n):
= 0
sum_ while sum_ <= 12:
+= np.random.randint(1, 7) # Roll a die
sum_
results.append(sum_)return results
def calculate_probabilities(results):
= {i: results.count(i) / len(results) for i in range(13, 19)}
probabilities return probabilities
def plot_probabilities(probabilities):
= probabilities.keys()
labels = probabilities.values()
values
= plt.subplots()
fig, ax = ax.bar(labels, values, color='skyblue')
bars
'Sum')
ax.set_xlabel('Probability')
ax.set_ylabel('Probabilities of Getting Sums from 13 to 18')
ax.set_title(
for bar in bars:
= bar.get_height()
height f'{height:.2f}',
ax.annotate(=(bar.get_x() + bar.get_width() / 2, height),
xy=(0, 3), # 3 points vertical offset
xytext="offset points",
textcoords='center', va='bottom')
ha
plt.show()
= 100000
n_simulations = monte_carlo_simulation(n=n_simulations)
results = calculate_probabilities(results)
probabilities plot_probabilities(probabilities)
Solution 2
The second approach is much more interesting. We can count the number of ways of arriving at a total between \(13\) and \(18\) such that every number involved in the sum is less than or equal to six and the running total till the second last number is less than or equal to \(12\). E.g. \(8+4+2\) is a valid way to reach \(14\) but not \(8+5+1\) because once you have \(8+5\) you have exceed \(12\) already. We make use of the concept of partitions (a partition of a non-negative integer \(n\), also called an integer partition, is a way of writing \(n\) as a sum of positive integers). If we denote by \(p(n,k)\) the partition of \(n\) where each number of the partition does not exceed \(k\), then the number of ways of reaching numbers \(13\) to \(18\) is given in the table below:
Total | Count |
---|---|
13 | p(13,6) |
14 | p(8,6) + p(9,6) + p(10,6) + p(11,6) + p(12,6) |
15 | p(9,6) + p(10,6) + p(11,6) + p(12,6) |
16 | p(10,6) + p(11,6) + p(12,6) |
17 | p(11,6) + p(12,6) |
18 | p(12,6) |
From the table it is easy to see that the probability of reaching \(14\) is higher than the probability of reaching the numbers \(15\) to \(18\). We need to compare \(sum_{i=8}^{12} p(i,6)\) with \(p(13,6)\) to confirm whether \(13\) or \(14\) is more likely. Using the code below we see that probability of reaching \(13\) is \(\approx 28\%\) and \(14\) is \(\approx 24\%\).
def generate_partitions(number, max_part=6):
def recurse(target, max_part, current_partition):
# Base case: when the target number is 0, yield the current partition
if target == 0:
yield list(current_partition)
else:
# Start from the last part used or max_part, whichever is smaller
= min(max_part, target)
start for next_part in range(start, 0, -1):
# Append next part and recurse
yield from recurse(target - next_part, next_part, current_partition + [next_part])
return list(recurse(number, max_part, []))
def calculate_probability(number):
from collections import Counter
from math import factorial
def partition_probability(partition):
= Counter(partition)
counter = len(partition)
l = factorial(l)
num for _,cnt in counter.items():
= num * 1/factorial(cnt)
num return num/6**l
= generate_partitions(number)
partitions = sum(partition_probability(p) for p in partitions)
total_probability return round(total_probability, 2)
print(f"Total probability for 13:", calculate_probability(13))
= 0
prob for n in range(8,13):
+= calculate_probability(n)/6
prob print(f"Total probability for 14:", prob)