A friend sent me a link to a blog post that discusses solutions to the Josephus problem in Perl, Ruby, and Python. Ouch! Those implementations hurt.
The Josephus problem is a simple discrete math puzzle:
Flavius Josephus was a roman historian of Jewish origin. During the Jewish-Roman wars of the first century AD, he was in a cave with fellow soldiers, 40 men in all, surrounded by enemy Roman troops. They decided to commit suicide by standing in a ring and counting off each third man. Each man so designated was to commit suicide…Josephus, not wanting to die, managed to place himself in the position of the last survivor.
In the general version of the problem, there are n soldiers numbered from 1 to n and each k-th soldier will be eliminated. The count starts from the first soldier. What is the number of the last survivor?
I think that the simplest way to solve this puzzle is to keep track of all alive soldiers and start eliminating every k-th solidier until we are left with just one soldier. In Haskell, I would implement this as follows:
survivor :: Int -> [a] -> a
survivor k soldiers = killnext k soldiers
where killnext l (x:xs) | Pre.null xs = x
| l == 1 = killnext k xs
| otherwise = killnext (l-1) (xs++[x])
main = do
args <- getArgs
let k = read (args!!1) :: Int
n = read (args!!2) :: Int
print $ survivor k [1..n]
Note that I am using Haskell’s lists to keep track of the alive soldiers. List append ++
is O(n2); which slows down the algorithm. The execution time (on my low power netbook) is (k is 3 in all experiments):
_n_ time (in s)
------ ---------------
4 0.00
40 0.01
400 0.02
4000 1.19
8000 6.04
So, it is better to replace list by a data structure that has O(1) append. I chose Data.Sequence
.
survivor :: Int -> [a] -> a
survivor k soldiers = viewnext k (Seq.fromList soldiers)
where viewnext l list = killnext l (Seq.viewl list)
killnext l (x:<xs) | Seq.null xs = x
| l == 1 = viewnext k xs
| otherwise = viewnext (l-1) (xs|>x)
The implementation is almost the same as before. The main difference is that I had to add a viewnext
accessor function to peek at the left element of the sequence. The execution time with this implementation is (as before k is 3 in all experiments):
_n_ time (in s)
----- ---------------
4 0.01
40 0.01
400 0.01
4000 0.02
8000 0.03
16000 0.05
Much better. A simple solution to a simple puzzle.
Hi,
nice clear and simple implementation.
I can’t compile the second implemention using Sequences. This is what i tried:
Note: i changed
to
I suppose the problem is the pattern match at
(x:<xs).
Chris
The following code works for me: https://gist.github.com/856072 I am using GHC 6.12.3, so perhaps something changed after that.
Hello, I’m rather new to Haskell so I was wondering how could I measure the time for my solution of the problem?
My solution can be found here: http://activedeveloper.info/flavius-josephus-in-haskell
I just used the unix shell command
time
.