The Josephus problem has existed for a long time, and its solutions have continually improved. However, no extremely efficient algorithm (within log complexity) is currently known.
Problem statement
n people numbered stand in a circle counterclockwise. Starting from person , count people counterclockwise from the current person each time, and remove the person reached. Who is the last person remaining?
This classic problem was posed by Josephus in the first century CE, although he considered only the case . Today, many efficient algorithms can solve it.
Process
Naive algorithm
The most naive algorithm is direct enumeration. Use a circular linked list to simulate the deletion process, repeating it times to obtain the answer. The complexity is .
Simple optimization
The process of finding the next person can be optimized with a segment tree. Build a segment tree over and record the number of people remaining in each interval. The current person's position and the position of the -th person after them can be found by binary search on the segment tree.
Linear algorithm
Let denote the answer to the Josephus problem with parameters . We have the recurrence
This is easy to derive. Starting from , count people and remove person . After the remaining people are solved, calculate the answer among the remaining people, then add a relative offset of to obtain the actual answer. The complexity is clearly .
Implementation
int josephus(int n, int k) {
int res = 0;
for (int i = 1; i <= n; ++i) res = (res + k) % i;
return res;
}
Logarithmic algorithm
When is small and is large, there is also an algorithm with complexity .
Since we delete one person after every steps, within one full circle we can delete people, leaving people. We are then at the position of person , which equals . We continue recursively and restore the relative position afterward. The reason for restoring the position is that every deletion removes the -th person counted, and the people after them inherit their numbers one by one. Thus, when counting around a circle of people, one position is miscounted for every people. If the result is less than , take it modulo in the circle of people whose multiples of have not yet been deleted; if the result is greater than or equal to , there is miscounted position for every group of k people, so it can be multiplied directly by . This gives the following algorithm:
Implementation
int josephus(int n, int k) {
if (n == 1) return 0;
if (k == 1) return n - 1;
if (k > n) return (josephus(n - 1, k) + k) % n; // Linear algorithm
int res = josephus(n - n / k, k);
res -= n % k;
if (res < 0)
res += n; // mod n
else
res += res / (k - 1); // Restore the position
return res;
}
It can be proved that this algorithm has complexity . Let be the number of recursive calls. At each step, the problem size becomes approximately , so
Solving this equation gives
We now prove that the complexity is .
Proof
Consider . We have
Therefore , so .
This page is mainly translated from the blog post Задача Иосифа and its English translation Josephus Problem. The Russian version is licensed under Public Domain + Leave a Link; the English version is licensed under CC-BY-SA 4.0.