Coin Flip
We begin by implementing a coin flip. As usual, we take Head as 1 and Tail as 0.
{{{id=1| def coin(): return randint(0,1) /// }}}Frequency Interpretation
One interpretation of probability is that if we repeat the event, then the probability is the frequency of occurrence.
Let us check!
{{{id=3| heads=0 for _ in range(10000): heads += coin() N(heads/10000) /// 0.502000000000000 }}}Close enough!
Counting Heads
Next, we have the probability that the number of Heads is $r$ in $k$ tosses is given by
$$ \frac{\binom{k}{r}}{2^k} $$
{{{id=5| def pheads(r,k): return binomial(k,r)/2^k /// }}}We can do the frequency check for this but we need to fix $k$ and $r$. Let $k=5$ and $r=1$.
{{{id=8| num=0 for _ in range(10000): heads=0 for _ in range(5): heads+=coin() if heads==1: num+=1 N(num/10000), N(pheads(1,5)) /// (0.161200000000000, 0.156250000000000) }}}Close enough!
Plotting
Let us also plot the Binomial distribution for large $k$ to see how it looks!
{{{id=16| values=[(r,N(pheads(r,100))) for r in range(101)] list_plot(values) /// }}}We note that all the probabilities are low! In fact the highest value is $0.08$.
Still this high probability is for 50 Heads as we expect! (Why?)
Moreover, the probability for a very small number of heads and a very small number of talks is very small. We can also expect this! (Why?)
Walks
We can also simulate the "walk". One step left if Tail, or one step right if Head.
How far away are we after 100 steps?
{{{id=9| def walk(n): steps=[2*coin()-1 for _ in range(n)] dist=[(i, sum(steps[:i])) for i in range(n+1)] return dist /// }}}A 100 step walk. The $x$ axis is the time axis.
{{{id=19| newwalk=walk(100) list_plot(newwalk,plotjoined=True) /// }}}We see that the walk can take us quite far from the starting point!
Visualising many walks
Let us save this walk and generate more and more walks.
{{{id=22| p=list_plot(newwalk,plotjoined=True,color="gray") /// }}}Each execution of the box below plots a new walk.
{{{id=14| oldwalk=newwalk newwalk=walk(100) p+=list_plot(oldwalk,plotjoined=True,color="gray") p+list_plot(newwalk,plotjoined=True) /// }}}Decreasing Step Size Walk
A different walk is one where we scale the step size with each step.
{{{id=18| def swalk(n): distance=0.0 stepsize=1.0 for _ in range(n): stepsize/=2 distance+=(2*coin()-1)*stepsize return distance /// }}}This way we can never get below -1 or above 1!
Let us check the frequency with which we find ourseleves in a a certain subinterval of $[-1,1]$.
{{{id=26| def check(a,b): assert (a>-1) and (b<1), "[a,b] must be a sub-interval of [-1,1]" num=0 for _ in range(5000): d=swalk(50) if aThis is similar to the uniform distribution on $[-1,1]$.
Simulation Dice
We can simulate a 6 sided die with 3 coins by declaring $1=(0,0,1)$, $2=(0,1,0)$, $3=(1,0,0)$, $4=(1,1,0)$, $5=(1,0,1)$, $6=(0,1,1)$.
Moreover, if we get $(0,0,0)$ or $(1,1,1)$ we throw again.
{{{id=29| diedict={(0,0,1):1, (0,1,0):2, (1,0,0):3, (1,1,0):4, (1,0,1):5, (0,1,1):6} def simdie(): throw=(coin(),coin(),coin()) if throw==(1,1,1) or throw==(0,0,0): return simdie() else: return diedict[throw] /// }}}We now do a frequency count to check that we are actually getting the frequency around $1/6$.
{{{id=30| diecounts=[0 for _ in range(7)] for _ in range(18000): diecounts[simdie()]+=1 diecounts[1:] /// [3027, 3065, 2927, 2945, 3047, 2989] }}}