Blake Rayvid - https://github.com/brayvid
Presented by Timandra Harkness and Matt Parker in Can you solve The Frog Problem?
Imagine some number of lilypads spanning a river in a line, and a frog at one bank. The frog wants to cross the river. To move forward, it selects one of the lilypads with uniform probability (or the far bank, which we include as an option), hops there, selects a new position closer to its destination, and repeats this process until it eventually reaches the far side.
For a given number of lilypads p, what is the expected number of jumps for the frog to make it across the river?
The code in this notebook uses functions from the scipy
, itertools
, numpy
and matplotlib
modules.
from scipy.special import comb # "a choose b," aka binomial coefficient
from itertools import combinations # lists the actual combinations
import numpy as np # matrix operations
from matplotlib import pyplot as pl # plots points
- Let
$p$ be the number of lilypads present. Note that the maximum number of jumps is$p+1$ . - Let
$k$ be the number of jumps the frog uses to cross the river,$1\leq k\leq p+1$ . - Let
$r={p \choose k-1}$ count the number of ways the frog can cross the river in exactly$k$ jumps.
The picture below depicts one of the
Landing on position
Thus, the overall probability of taking that particular route as opposed to any other is
The function listRoutes(p,k)
returns all routes for a given numpy.array
of size
def listRoutes(p, k):
rows = comb(p, k - 1, 1)
cols = k + 1
M = [[0 for x in range(cols)] for y in range(rows)]
C = list(combinations(range(1, p + 1), k - 1))
for l in range(k - 1):
for r in range(rows):
M[r][l + 1] = C[r][l]
for r in range(rows):
M[r][cols - 1] = p + 1
return np.array(M)
Following the earlier example, here is the matrix for
listRoutes(5,3)
array([[0, 1, 2, 6],
[0, 1, 3, 6],
[0, 1, 4, 6],
[0, 1, 5, 6],
[0, 2, 3, 6],
[0, 2, 4, 6],
[0, 2, 5, 6],
[0, 3, 4, 6],
[0, 3, 5, 6],
[0, 4, 5, 6]])
There are indeed
Given that at each step along the route all remaining positions have an equal chance of being selected next: if the frog is at position
To find the probability
where
Since each route is unique and the list is exhaustive, the probability
The function pathLengthProbability(p,k,m)
implements the above formula, returning
def pathLengthProbability(p, k, m):
M = m[:,:-1]
N = np.full((M.shape[0], k), p + 1)
minusNM = np.subtract(N, M)
overNsubM = np.divide(np.ones((M.shape[0], k)), minusNM)
s = np.sum(np.prod(overNsubM, axis = 1))
return s
The function lengthProb(p,k)
simply wraps pathLengthProbability(p,k,m)
and listRoutes(n,k)
together for ease of use, returning
def lengthProb(p, k):
if k <= (p + 1) and k >= 1:
return pathLengthProbability(p, k, listRoutes(p, k))
else:
print("Bad input")
return 0
The expectation of a discrete random variable
Therefore the solution to the problem, the number of steps in which we expect the frog to cross the river for a given
Note: $M$ will change for each term in the outer sum. A new one must be computed for each value of $k$.
The function expectedSteps(p)
returns the above quantity given some
def expectedSteps(p):
n = p + 1
s = 0
for k in range(1, n + 1):
s += (k * lengthProb(p, k))
return s
lengthProb(4,1)
0.2
p = 5
n = p + 1 # max possible jumps
s = 0
for k in range(1, n + 1):
s += lengthProb(p, k)
print(s)
1.0
expectedSteps(1)
1.5
# Values of p to check
lowP = 0
highP = 20
# Plot points
x = range(lowP, highP + 1)
y = []
for p in x:
y.append(expectedSteps(p))
pl.plot(x,y,'ko')
pl.xlabel('Number of lillypads')
pl.ylabel('Expected number of jumps')
pl.title('Expected jumps vs. number of lillypads')
pl.xticks(np.arange(min(x), max(x) + 1, 1))
pl.yticks(np.arange(min(y), max(y) + 0.25, 0.25))
pl.show()