Roll n fair dice, each with faces 1..sides. Write two functions that return exact answers with fractions.Fraction:
dice_sum_distribution(n: int, sides: int) -> dict[int, Fraction]: map every possible total to its probability. Only include totals that can actually happen; the probabilities must add up to exactly 1.expected_value(dist: dict[int, Fraction]) -> Fraction: the expected valueΣ value · P(value)of any distribution given in that form.
dice_sum_distribution(1, 4) # {1: 1/4, 2: 1/4, 3: 1/4, 4: 1/4}
dice_sum_distribution(2, 3) # {2: 1/9, 3: 2/9, 4: 3/9, 5: 2/9, 6: 1/9}
dice_sum_distribution(0, 6) # {0: 1} (no dice: the total is always 0)
expected_value(dice_sum_distribution(2, 6)) # Fraction(7, 1)
Don't list all sides ** n outcomes; the tests use up to 30 dice. Build the answer one die at a time: start from {0: 1}, and for each new die, every current total t with probability p spreads p / sides onto t + 1, ..., t + sides.
Constraints: 0 <= n <= 30, 1 <= sides <= 20.
Show hint
Adding one independent die is a convolution: new[t + f] += old[t] * Fraction(1, sides) for every face f.