The Josephus Problem — Practice Tasks¶
All tasks must be solved in Go, Java, and Python. Each task ships with starter code or a precise spec in all three languages. Implement the Josephus logic and validate against the evaluation criteria. Always test against a brute-force list-simulation oracle on small inputs before trusting the recurrence, the closed form, or the Fenwick order.
Beginner Tasks (5)¶
Task 1 — Survivor by the O(n) recurrence¶
Problem. Implement josephus(n, k) returning the 1-indexed surviving seat for n people, step k, using the recurrence J(1) = 0, J(n) = (J(n-1) + k) mod n. Add 1 at the end.
Input / Output spec. - Read n and k. - Print the single 1-indexed survivor seat.
Constraints. - 1 ≤ n ≤ 10^7, 1 ≤ k ≤ 10^9. - Use 64-bit and reduce k mod m per step to avoid overflow.
Hint. Loop m from 2 to n; res = (res + k % m) % m. Return res + 1.
Starter — Go.
package main
import "fmt"
func josephus(n, k int) int {
// TODO: recurrence, return 1-indexed survivor
return 0
}
func main() {
var n, k int
fmt.Scan(&n, &k)
fmt.Println(josephus(n, k))
}
Starter — Java.
import java.util.*;
public class Task1 {
static int josephus(int n, int k) {
// TODO
return 0;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt(), k = sc.nextInt();
System.out.println(josephus(n, k));
}
}
Starter — Python.
import sys
def josephus(n, k):
# TODO
return 0
def main():
data = sys.stdin.read().split()
n, k = int(data[0]), int(data[1])
print(josephus(n, k))
if __name__ == "__main__":
main()
Evaluation criteria. - n = 5, k = 2 → 3; n = 7, k = 3 → 4; n = 1, k = 5 → 1. - Matches a brute-force list simulation for all n ≤ 50, k ≤ 12. - O(n) time, O(1) space.
Task 2 — k = 2 closed form (bit rotation)¶
Problem. Implement josephus2(n) returning the 1-indexed survivor for step k = 2 in O(1) using 2·(n − 2^⌊log₂ n⌋) + 1.
Input / Output spec. - Read n. Print the survivor seat.
Constraints. 1 ≤ n ≤ 10^18.
Hint. Use the highest-set-bit intrinsic: bits.Len64, Long.highestOneBit, int.bit_length.
Starter — Python.
def josephus2(n):
# TODO: L = largest power of two <= n; return 2*(n-L)+1
return 0
if __name__ == "__main__":
print(josephus2(41)) # 19
Worked check. n = 41 = 101001₂; rotate the leading 1 to the end → 010011₂ = 19. Power-of-two n (8, 16, 32) → seat 1.
Evaluation criteria. - n = 41 → 19, n = 8 → 1, n = 1 → 1. - The survivor is always odd (assert it). - Matches the O(n) recurrence with k = 2 for all n ≤ 1000.
Task 3 — Full elimination order by simulation¶
Problem. Return the full elimination order (1-indexed labels, in removal order) for n people, step k, by direct list simulation. The last element is the survivor.
Input / Output spec. - Read n, k. Print the order space-separated.
Constraints. 1 ≤ n ≤ 5000, 1 ≤ k ≤ 10^6.
Hint. Keep a list [1..n] and an index idx = 0. Each round: idx = (idx + k - 1) % len(people), then remove people[idx] and append it to the order.
Reference oracle (Python) — use this to validate.
def order_oracle(n, k):
people = list(range(1, n + 1))
idx = 0
order = []
while people:
idx = (idx + k - 1) % len(people)
order.append(people.pop(idx))
return order
Evaluation criteria. - n = 5, k = 2 → [2, 4, 1, 5, 3]. - The last element equals josephus(n, k) from Task 1. - Matches the oracle for all small n, k.
Task 4 — Position to survive¶
Problem. Given n and k, print the seat you should stand in to survive (1-indexed). This is exactly J(n, k) + 1.
Input / Output spec. - Read n, k. Print the safe seat.
Constraints. 1 ≤ n ≤ 10^7, 1 ≤ k ≤ 10^9.
Hint. Reuse the Task 1 recurrence; the "position to survive" is the survivor seat.
Evaluation criteria. - Standing at the printed seat in a simulation survives every time. - Matches Task 1 output (they are the same query). - Handles k = 1 (safe seat is n) and n = 1 (safe seat is 1).
Task 5 — k = 1 special case¶
Problem. For k = 1, prove by code that the survivor is always person n. Implement survivor_k1(n) returning n, and verify it against both the recurrence and a simulation.
Input / Output spec. - Read n. Print the survivor (should be n).
Constraints. 1 ≤ n ≤ 10^9.
Hint. With k = 1, people are eliminated in order 1, 2, …, n−1. The recurrence gives J(n, 1) = n − 1 (0-indexed), seat n.
Evaluation criteria. - Returns n for all n. - Matches josephus(n, 1) from Task 1 (which must equal n). - Matches a list simulation with k = 1 for n ≤ 50.
Intermediate Tasks (5)¶
Task 6 — Huge n, small k survivor (O(k log n))¶
Problem. Return the 0-indexed survivor for huge n (up to 10^18) and small k, in O(k log n), by batching the non-wrapping steps of the recurrence.
Constraints. 1 ≤ n ≤ 10^18, 2 ≤ k ≤ 10^4.
Hint. While res + k < m, skip cnt = (m − res − 1) / k sizes at once: res += cnt·k, m += cnt (capped so m + cnt ≤ n). Otherwise res = (res + k) % m, m++.
Reference oracle (Python).
Starter — Go.
package main
import "fmt"
func josephusFast(n, k int64) int64 {
if k == 1 {
return n - 1
}
// TODO: batch non-wrapping steps; cap cnt so m+cnt <= n
return 0
}
func main() {
var n, k int64
fmt.Scan(&n, &k)
fmt.Println(josephusFast(n, k))
}
Starter — Java.
import java.util.*;
public class Task6 {
static long josephusFast(long n, long k) {
if (k == 1) return n - 1;
// TODO
return 0;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
long n = sc.nextLong(), k = sc.nextLong();
System.out.println(josephusFast(n, k));
}
}
Evaluation criteria. - Matches survivor_oracle for all n ≤ 5000, k ≤ 50. - Completes instantly for n = 10^18, k = 7. - Correctly caps cnt so m never exceeds n (no infinite loop, no overshoot).
Task 7 — The m-th person eliminated¶
Problem. Given n, k, and m (1 ≤ m ≤ n), return the 1-indexed label of the m-th person eliminated. Use a Fenwick tree so each removal is O(log n).
Constraints. 1 ≤ n ≤ 10^6, 1 ≤ k ≤ 10^9, 1 ≤ m ≤ n.
Hint. Run the Fenwick select-and-delete m times; return the m-th label found. m = n gives the survivor.
Reference oracle (Python).
def mth_eliminated_oracle(n, k, m):
people = list(range(1, n + 1))
idx = 0
for _ in range(m):
idx = (idx + k - 1) % len(people)
victim = people.pop(idx)
return victim
Evaluation criteria. - n = 5, k = 2, m = 1 → 2; m = 5 → 3 (the survivor). - Matches the oracle for all small n, k, m. - O(m log n); m = n agrees with the Task 1 survivor.
Task 8 — Variable step per round¶
Problem. Given n and a list of steps k_2, k_3, …, k_n (the step used when the circle shrinks from size m to m−1), return the 1-indexed survivor. Use J(n) = (J(n-1) + k_n) mod n.
Constraints. 1 ≤ n ≤ 10^6, each k_m in [1, 10^9].
Hint. Same loop as Task 1, but read the step from the array for each m. There is no closed form.
Reference oracle (Python).
def variable_step_oracle(n, steps): # steps[m] used at size m+2
people = list(range(1, n + 1))
idx = 0
size = n
while len(people) > 1:
k = steps[len(people) - 2] # step for current size
idx = (idx + k - 1) % len(people)
people.pop(idx)
return people[0]
Evaluation criteria. - Constant-step input reproduces Task 1. - Matches the variable-step simulation oracle for small n. - O(n) time.
Task 9 — Choose-your-seat search¶
Problem. Given n and a desired survivor seat s, find the smallest step k in [1, K_max] such that J(n, k) + 1 = s. Return -1 if none exists in range.
Constraints. 1 ≤ n ≤ 10^4, 1 ≤ s ≤ n, 1 ≤ K_max ≤ 10^4.
Hint. For each candidate k from 1 to K_max, evaluate josephus(n, k) (Task 1) and check if +1 == s. Return the first match.
Evaluation criteria. - Returns a k whose survivor seat is exactly s (verify by re-running the recurrence). - Returns -1 when no k ≤ K_max works. - Handles s = n (e.g. k = 1 always works).
Task 10 — Survivor with a non-zero start position¶
Problem. Given n, k, and a starting seat start (1-indexed, where the first count begins), return the 1-indexed survivor. The canonical recurrence assumes start = 1.
Constraints. 1 ≤ n ≤ 10^7, 1 ≤ k ≤ 10^9, 1 ≤ start ≤ n.
Hint. Solve the canonical problem (start = 1) to get J(n, k) (0-indexed), then rotate: the seat is ((J(n, k) + (start − 1)) mod n) + 1.
Reference oracle (Python).
def start_oracle(n, k, start):
people = list(range(1, n + 1))
idx = start - 1
while len(people) > 1:
idx = (idx + k - 1) % len(people)
people.pop(idx)
if idx == len(people): # wrap pointer after pop at the end
idx = 0
return people[0]
Evaluation criteria. - start = 1 reproduces Task 1. - Matches the start-aware simulation oracle for small n, k, start. - The rotation is applied exactly once.
Advanced Tasks (5)¶
Task 11 — Two independent verifications of the k = 2 form¶
Problem. Implement the k = 2 survivor two independent ways — (a) the arithmetic closed form 2(n − 2^⌊log₂ n⌋) + 1, and (b) the bit-rotation of n's binary string — and assert they agree for all n in [1, N].
Constraints. 1 ≤ N ≤ 10^6.
Hint. For the rotation: take n's bit string (leading bit is 1), move the leading 1 to the rightmost position, parse back to an integer.
Starter — Python.
def closed_form(n):
L = 1 << (n.bit_length() - 1)
return 2 * (n - L) + 1
def bit_rotation(n):
b = bin(n)[2:] # binary string, leading '1'
rotated = b[1:] + b[0] # move leading bit to the end
return int(rotated, 2)
if __name__ == "__main__":
for n in range(1, 1_000_000):
assert closed_form(n) == bit_rotation(n), n
print("closed form and bit rotation agree")
Evaluation criteria. - The two methods agree for every n ≤ N. - Both agree with the O(n) recurrence (k = 2) for n ≤ 1000. - Both produce odd results and preserve n's popcount.
Task 12 — Fenwick elimination order at scale¶
Problem. Produce the full elimination order for n up to 10^6 in O(n log n) using a Fenwick tree with an O(log n) select-walk (no array-middle removal).
Constraints. 1 ≤ n ≤ 10^6, 1 ≤ k ≤ 10^9.
Hint. Fenwick stores 1 for alive. Maintain alive and a 0-indexed living pointer cur; victim living-rank is (cur + k − 1) mod alive; find its slot with the Fenwick walk; update(idx, −1).
Evaluation criteria. - Matches the list-simulation order (Task 3) for all small n, k. - Runs in O(n log n); completes for n = 10^6 in well under a second (Go/Java). - The pointer-to-rank arithmetic is tested entrywise against the simulation.
Task 13 — Last r survivors¶
Problem. Given n, k, and r (1 ≤ r ≤ n), return the set of r people remaining when the process stops with r survivors (in seat order). Use the Fenwick order and take the last r eliminated... wait — the survivors are the people never eliminated, i.e. the labels not in the first n − r removals.
Constraints. 1 ≤ n ≤ 10^6, 1 ≤ k ≤ 10^9, 1 ≤ r ≤ n.
Hint. Run the Fenwick elimination for n − r removals; the remaining living slots are the r survivors. Read them by scanning which slots are still 1.
Reference oracle (Python).
def last_r_oracle(n, k, r):
people = list(range(1, n + 1))
idx = 0
while len(people) > r:
idx = (idx + k - 1) % len(people)
people.pop(idx)
if idx == len(people):
idx = 0
return sorted(people)
Evaluation criteria. - r = 1 reproduces the single survivor (Task 1). - Matches the oracle for small n, k, r. - Stops after exactly n − r removals.
Task 14 — Inverse k = 2 query¶
Problem. Given an odd survivor seat s produced by the k = 2 process, recover the smallest n whose k = 2 survivor is s. (The k = 2 survivor is always odd, and the map is the bit rotation.)
Constraints. 1 ≤ s ≤ 10^18, s odd.
Hint. The forward map is "rotate the leading 1 of n to the end". The inverse is "rotate the trailing 1 of s to the front": take s's b-bit string ending in 1, move that last 1 to the most-significant position. Verify with the forward closed form.
Evaluation criteria. - For s = 19, recovers n = 41 (and josephus2(41) == 19). - Rejects or flags even s (impossible for k = 2). - forward(inverse(s)) == s for all odd s in a test range.
Task 15 — Decide the right Josephus method¶
Problem. Given constraints (n, k, query_type), implement classify(n, k, query_type) returning one of: CLOSED_FORM (k = 2, survivor), BATCHED (small k, huge n, survivor), RECURRENCE (moderate n, survivor), FENWICK_ORDER (full order or m-th eliminated), or INFEASIBLE (general large k with huge n, survivor). Justify each.
Constraints / cases to handle. - k = 2, survivor → CLOSED_FORM. - k ≤ 10^4, n > 10^7, survivor → BATCHED. - n ≤ 10^7, survivor, any k → RECURRENCE. - query is order or m-th eliminated → FENWICK_ORDER. - k > 10^4, n > 10^9, survivor → INFEASIBLE.
Hint. Encode the decision rules from middle.md and senior.md. The INFEASIBLE branch must explicitly note that general large-k, huge-n survivor has no known sublinear algorithm.
Evaluation criteria. - Correctly classifies all five canonical cases. - Cites the right complexity for each (O(1), O(k log n), O(n), O(n log n)). - The INFEASIBLE branch explains the missing sublinear method.
Benchmark Task¶
Task B — Recurrence vs simulation vs Fenwick crossover¶
Problem. Empirically compare three approaches for the survivor query and one for the order query on a fixed n:
- (a) List simulation, survivor:
O(n·k). - (b)
O(n)recurrence, survivor:O(n). - (c)
O(k log n)batched, survivor: for smallk. - (d) Fenwick order:
O(n log n)for the full elimination order.
Measure wall-clock time for n ∈ {10³, 10⁴, 10⁵, 10⁶} with k = 5, and report which method wins at each size. Use a fixed input so all three languages match.
Starter — Python.
import time
def list_sim_survivor(n, k):
# TODO: O(n*k) list simulation, return survivor
return 0
def recurrence_survivor(n, k):
res = 0
for m in range(2, n + 1):
res = (res + k) % m
return res
def batched_survivor(n, k):
# TODO: O(k log n) batched recurrence
return 0
def bench(fn, *args):
start = time.perf_counter()
fn(*args)
return (time.perf_counter() - start) * 1000.0
def main():
k = 5
print("n list_ms recur_ms batched_ms")
for n in [1000, 10000, 100000, 1000000]:
ls = bench(list_sim_survivor, n, k) if n <= 100000 else float("nan")
rc = bench(recurrence_survivor, n, k)
bt = bench(batched_survivor, n, k)
print(f"{n:<9d} {ls:<11.2f} {rc:<11.2f} {bt:<11.2f}")
if __name__ == "__main__":
main()
Evaluation criteria. - Same input produces the same survivor across Go, Java, and Python. - List simulation (a) is far slower than the recurrence (b) as n grows; the batched method (c) is fastest for survivor-only at large n. - The Fenwick order (d) is O(n log n) and the only feasible way to get the full order at n = 10⁶. - Report mean over at least 3 runs; do not time input generation. - Writeup: note the measured crossover and confirm the O(n·k) simulation becomes impractical well before the recurrence does.
General Guidance for All Tasks¶
- Always validate against the brute-force simulation oracle first. Every survivor/order task ships with (or references) a slow list-simulation oracle. Run it on small
nand a spread ofk, diff entrywise, and only then trust the fast version on large inputs. - Pin the index convention as a named decision. The recurrence is 0-indexed; humans want 1-indexed; convert with a single explicit
+1. Decide once and document it. - Get the base case right.
J(1) = 0for the recurrence; the loop runsmfrom2ton. Do not initializeres = 1. - Mind overflow. In Go/Java use 64-bit and reduce
res = (res + k % m) % m; capcntin the batched method som + cnt ≤ n. Python big ints avoid the issue. - Use the right tool for the scale.
k = 2→ closed form (O(1)); smallk, hugen→ batched (O(k log n)); moderaten→ recurrence (O(n)); order → Fenwick (O(n log n)). - The
k = 2survivor is always odd and sharesn's popcount — cheap invariants to assert. - Separate survivor from order in your API; they are different outputs with different costs.
Each task must be implemented and tested in Go, Java, and Python, with identical outputs across all three on the same inputs.