first off: the total possible roll combinations will always be s**n
, so you don't need to store a list of all possibilities in order to get it's length. Similarly you can just keep a running total of desired outcomes instead of keeping a list of them to save on memory space but it still won't speed up the function a whole lot:
def probability(n, s, t):
all_rolls = itertools.product(range(1,s+1), repeat=n) #no list, leave it a generator
target_rolls=sum(1 for l in all_rolls if sum(l)==t) #just total them up
return round(target_rolls/s**n, 4)
A much more efficient way of calculating the possibilities is with a dict
and some clever iteration. Each dictionary will use the roll value as keys and frequency as value, each iteration the prev
will be this dict for the previous X dice and cur
will be updated from it with the addition of another die:
import collections
def probability(n, s, t):
prev = {0:1} #previous roll is 0 for first time
for _ in range(n):
cur = collections.defaultdict(int) #current probability
for r,times in prev.items():
for i in range(1,s+1):
#if r occured `times` times in the last iteration then
#r+i have `times` more possibilities for the current iteration.
cur[r+i]+=times
prev = cur #use this for the next iteration
return cur[t] / s**n
#return round(cur[t] / s**n , 4)
note 1: since cur
is a defaultdict trying to look up a number that is not not possible with the given input will return 0
note 2: since this method puts together a dictionary with all possible outcomes you can return cur
and do the calculation for multiple different possible outcomes on the same dice rolls.