This is a solution to this problem:
A drunken sailor returns to his ship via a plank 15 paces long and 7 paces wide. With each step he has an equal chance of stepping forward, left, right, or standing still. What is the probability that he returns safely to his ship?
The article shows two different solutions, one using a Monte Carlo approach and the other, Markov chains. The Monte Carlo approach is pretty trivial; here’s how I did it using Python:
def walk(): pos = array([4, 0]) while 1: if pos[1] == 16: return True elif not 0 < pos[0] < 8: return False pos += choice([ array([ 0, 1]), # forward array([ 1, 0]), # right array([-1, 0]), # left array([ 0, 0]), # still ])
This is our random walk. We keep updating the position of the sailor
until he either reaches the ship of falls into water. We can estimate
the chance that the sailor will reach the boat by running this
function several times and counting the percentage that returns
True
:
REALIZATIONS = 10000 successes = 0 for i in range(REALIZATIONS): if walk(): successes += 1 print float(successes)/REALIZATIONS
For 10 thousand realizations I get a probability of 12% that the
sailor can cross the plank. But I’m more interested in the Markov
chain solution. The idea behind this solution is to create a matrix of
the probabilities of going from a place A to a point B. In order to do
this I create an array of shape (7+2, 15+1, 7+2, 15+1)
. This array
includes two additional side points, for water, and one additional
point along the plank, representing the boat. In this array, the value
at the position (i0, j0, i1, j1)
represents the probability of a
person at point (i0, j0)
going to the position (i1, j1)
. We start
with an empty arrays of zeros:
WIDTH = 7 LENGTH = 15 markov = zeros((WIDTH+2, LENGTH+1, WIDTH+2, LENGTH+1), 'f')
Now let’s set the probabilities for the plank:
for i in range(1, WIDTH+1): for j in range(LENGTH): markov[i, j, i, j+1] = 0.25 # forward markov[i, j, i+1, j] = 0.25 # right markov[i, j, i-1, j] = 0.25 # left markov[i, j, i, j] = 0.25 # still
So, if the sailor is in the point (i, j)
inside the plank it has a
25% chance of moving to point (i+1, j)
, ie, to the right. We also
set the probabilities for when the sailor is on the water; in this
case, he will stay there:
# when in water, stay for j in range(LENGTH+1): markov[0, j, 0, j] = 1 markov[WIDTH+1, j, WIDTH+1, j] = 1
And we do the same when he reaches the boat:
# when in the ship, stay for i in range(WIDTH+2): markov[i, LENGTH, i, LENGTH] = 1
We also reshape our array, making it 2D. It will still keep the probabilistic correspondence between point A and B, but now our space dimension has been turned into 1D:
markov.shape = ((WIDTH+2)*(LENGTH+1), (WIDTH+2)*(LENGTH+1))
The rest now is easy. The transition matrix we just built has the nice
property that it represents the chance of going from one point to the
other, and if we keep multiplying it by itself n
times we have the
chance of reaching point B after n
steps from point A. So we just
need to convert our array into a matrix (there’s a difference in
Numpy) and take the power to a resonable number of steps:
MAXSTEPS = 200 result = matrix(markov)**MAXSTEPS result = array(result).reshape(WIDTH+2, LENGTH+1, WIDTH+2, LENGTH+1)
What we want is the chance of a sailor leaving from point (4, 0)
to
reach the boat at any point in (i, 15)
. We can get this easily:
print sum(result[4, 0, :, 15])
And the result is 15%. Pretty cool.