Josephus Problem

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 0,1,⋯ ,n−10,1,\cdots, n-1 stand in a circle counterclockwise. Starting from person 00, count kk 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 k=2k=2. 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 n−1n-1 times to obtain the answer. The complexity is Θ(n2)\Theta (n^2).

Simple optimization

The process of finding the next person can be optimized with a segment tree. Build a segment tree over 0,1,⋯ ,n−10,1,\cdots, n-1 and record the number of people remaining in each interval. The current person's position and the position of the kk-th person after them can be found by binary search on the segment tree.

Linear algorithm

Let Jn,kJ_{n,k} denote the answer to the Josephus problem with parameters n,kn,k. We have the recurrence

Jn,k=(Jn−1,k+k) mod nJ_{n,k}=(J_{n-1,k}+k)\bmod n

This is easy to derive. Starting from 00, count kk people and remove person k−1k-1. After the remaining n−1n-1 people are solved, calculate the answer among the remaining n−1n-1 people, then add a relative offset of kk to obtain the actual answer. The complexity is clearly Θ(n)\Theta (n).

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 kk is small and nn is large, there is also an algorithm with complexity Θ(klog⁡n)\Theta (k\log n).

Since we delete one person after every kk steps, within one full circle we can delete ⌊nk⌋\left\lfloor\frac{n}{k}\right\rfloor people, leaving n−⌊nk⌋n-\left\lfloor\frac{n}{k}\right\rfloor people. We are then at the position of person ⌊nk⌋⋅k\left\lfloor\frac{n}{k}\right\rfloor\cdot k, which equals n−n mod kn-n\bmod k. We continue recursively and restore the relative position afterward. The reason for restoring the position is that every deletion removes the kk-th person counted, and the people after them inherit their numbers one by one. Thus, when counting around a circle of n−⌊nk⌋n-\left\lfloor\frac{n}{k}\right\rfloor people, one position is miscounted for every kk people. If the result is less than 00, take it modulo nn in the circle of nn people whose multiples of kk have not yet been deleted; if the result is greater than or equal to 00, there is 11 miscounted position for every group of k people, so it can be multiplied directly by kk−1\frac{k}{k-1}. 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 Θ(klog⁡n)\Theta (k\log n). Let xx be the number of recursive calls. At each step, the problem size becomes approximately n(1−1k)\displaystyle n\left(1-\frac{1}{k}\right), so

n(1−1k)x=1n\left(1-\frac{1}{k}\right)^x=1

Solving this equation gives

x=−ln⁡nln⁡(1−1k)x=-\frac{\ln n}{\ln\left(1-\frac{1}{k}\right)}

We now prove that the complexity is Θ(klog⁡n)\Theta (k\log n).

Proof

Consider lim⁡k→∞klog⁡(1−1k)\displaystyle \lim _{k \rightarrow \infty} k \log \left(1-\frac{1}{k}\right). We have

lim⁡k→∞klog⁡(1−1k)=lim⁡k→∞log⁡(1−1k)1/k=lim⁡k→∞ddklog⁡(1−1k)ddk(1k)=lim⁡k→∞1k2(1−1k)−1k2=lim⁡k→∞−kk−1=−lim⁡k→∞11−1k=−1\begin{aligned} \lim _{k \rightarrow \infty} k \log \left(1-\frac{1}{k}\right)&=\lim _{k \rightarrow \infty} \frac{\log \left(1-\frac{1}{k}\right)}{1 / k}\\ &=\lim _{k \rightarrow \infty} \frac{\frac{\mathrm d}{\mathrm d k} \log \left(1-\frac{1}{k}\right)}{\frac{\mathrm d}{\mathrm d k}\left(\frac{1}{k}\right)}\\ &=\lim _{k \rightarrow \infty} \frac{\frac{1}{k^{2}\left(1-\frac{1}{k}\right)}}{-\frac{1}{k^{2}}}\\ &=\lim _{k \rightarrow \infty}-\frac{k}{k-1}\\ &=-\lim _{k \rightarrow \infty} \frac{1}{1-\frac{1}{k}}\\ &=-1 \end{aligned}

Therefore x∼kln⁡n,k→∞x \sim k \ln n, k\to \infty, so −ln⁡nln⁡(1−1k)=Θ(klog⁡n)-\dfrac{\ln n}{\ln\left(1-\frac{1}{k}\right)}= \Theta (k\log n).

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.

This content comes from OI Wiki: Josephus Problem. See the original for authors and revision history. AIOJ has adapted the formatting, in-site links, and resource paths.

Content is shared under CC BY-SA 4.0 and SATA license; additional terms are governed by the original notice. Thanks to the OI Wiki authors and contributors; per the SATA terms, please visit the OI Wiki repository and star it to show your support.