"""Exact probability model, not observed shots or trained xG predictions.""" import itertools,json from fractions import Fraction as F def model(probs): if not probs or len(probs)>20 or any(p<0 or p>1 for p in probs):raise ValueError('1..20probabilities in[0,1]required') distribution=[F(1)] for p in probs: nxt=[F(0)]*(len(distribution)+1) for k,v in enumerate(distribution):nxt[k]+=v*(1-p);nxt[k+1]+=v*p distribution=nxt assert sum(distribution)==1 and sum(k*v for k,v in enumerate(distribution))==sum(probs) # Independent exhaustive enumeration of every invented success/failure sequence. brute=[F(0)]*(len(probs)+1) for bits in itertools.product([0,1],repeat=len(probs)): chance=F(1) for bit,p in zip(bits,probs):chance*=p if bit else 1-p brute[sum(bits)]+=chance assert brute==distribution return {'expected_goals':str(sum(probs)),'no_goal':str(distribution[0]),'at_least_one':str(1-distribution[0]),'distribution':[str(x)for x in distribution]} def calculate(): inputs=[('one_half',['0.5']),('five_tenths',['0.1']*5),('two_halves',['0.5']*2),('ten_tenths',['0.1']*10),('certain_zero',['0','0']),('certain_one',['0','1'])] cases=[{'id':name,'probabilities':p,'shots':len(p),**model([F(s)for s in p])}for name,p in inputs] assert cases[0]['expected_goals']==cases[1]['expected_goals']=='1/2' assert F(cases[0]['at_least_one'])==F(1,2) and F(cases[1]['at_least_one'])==F(40951,100000) assert F(cases[2]['no_goal'])==F(1,4)and F(cases[3]['no_goal'])==F(3486784401,10000000000) return {'kind':'invented_independent_bernoulli_shots_not_match_prediction','calculated_on':'2026-11-02','cases':cases,'method':'IndependentBernoulli assumptions; exactFraction dynamicprogramming crosschecked exhaustive2^Noutcomes. Expectedgoals sum doesnotrequireindependence; productanddistribution here do.','scope':'Probabilities are chosen educational inputs, not output from a fitted xG model. Actual shots/rebounds may be dependent and future shot opportunities change with game state. Notmatchobservation, scoreforecast, playerability rating or betting tool.'} if __name__=='__main__':print(json.dumps(calculate(),ensure_ascii=False,indent=2))