Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 82 additions & 0 deletions src/main/java/com/thealgorithms/recursion/Josephus.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package com.thealgorithms.recursion;

/**
* Josephus - Solves the Josephus problem using recursion
*
* The Josephus problem: n people numbered 1..n stand in a circle. Starting from
* position 1, every k-th person is eliminated until one remains. This class
* provides a recursive solution to compute the survivor's position (1-based).
*
* Recurrence (0-based): J(1,k) = 0; J(n,k) = (J(n-1,k) + k) % n
* Convert to 1-based by adding 1.
*
* @author ritesh-3822
* @see <a href="https://en.wikipedia.org/wiki/Josephus_problem">Josephus problem</a>
*/
public final class Josephus {

private Josephus() {
// prevent instantiation
}
/**
* Returns the 1-based position of the survivor for given n and k.
*
* @param n number of people (must be > 0)
* @param k step count (must be > 0)
* @return 1-based index of the survivor
* @throws IllegalArgumentException if n <= 0 or k <= 0
*/
public static int getJosephus(int n, int k) {
if (n <= 0) {
throw new IllegalArgumentException("n must be positive");
}
if (k <= 0) {
throw new IllegalArgumentException("k must be positive");
}
// compute zero-based result and convert to 1-based
return josephusZeroBased(n, k) + 1;
}

/**
* Prints the survivor position (1-based) for given n and k.
*
* @param n number of people (must be > 0)
* @param k step count (must be > 0)
* @throws IllegalArgumentException if n <= 0 or k <= 0
*/
public static void printJosephus(int n, int k) {
int survivor = getJosephus(n, k);
System.out.println(survivor);
}

/**
* Recursive helper that returns the zero-based survivor index.
*
* @param n number of people
* @param k step
* @return zero-based survivor index
*/
private static int josephusZeroBased(int n, int k) {
// Base case
if (n == 1) {
return 0;
}
// Recurrence
return (josephusZeroBased(n - 1, k) + k) % n;
}

/**
* Demo method to show usage
*
* @param args command line arguments
*/
public static void main(String[] args) {
int n = 7;
int k = 3;

System.out.println("Josephus problem demo:");
System.out.printf("n = %d, k = %d%n", n, k);
int survivor = getJosephus(n, k);
System.out.println("Survivor (1-based position): " + survivor);
}
}
96 changes: 96 additions & 0 deletions src/test/java/com/thealgorithms/recursion/JosephusTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package com.thealgorithms.recursion;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import org.junit.jupiter.api.Test;

/**
* Test class for Josephus problem implementation
*
* @author ritesh-3822
* @see Josephus
*/
class JosephusTest {

@Test
void testNOneAnyK() {
// single person -> survivor is 1
assertEquals(1, Josephus.getJosephus(1, 1));
assertEquals(1, Josephus.getJosephus(1, 5));
assertEquals(1, Josephus.getJosephus(1, 100));
}

@Test
void testSmallCases() {
// Known small results
assertEquals(3, Josephus.getJosephus(5, 2)); // classic: n=5,k=2 -> 3
assertEquals(4, Josephus.getJosephus(7, 3)); // classic: n=7,k=3 -> 4
assertEquals(5, Josephus.getJosephus(10, 2)); // n=10,k=2 -> 5
}

@Test
void testLargerKnown() {
// Known classic example
assertEquals(28, Josephus.getJosephus(40, 3)); // classic result
}

@Test
void testVariousKValues() {
assertEquals(1, Josephus.getJosephus(2, 2)); // persons 1..2, k=2 -> survivor 1
assertEquals(2, Josephus.getJosephus(2, 1)); // k=1 eliminates in order -> last is 2
assertEquals(4, Josephus.getJosephus(8, 3));
}

@Test
void testLargeNPerformance() {
// sanity for large n: should complete quickly (recursive depth = n)
int survivor = Josephus.getJosephus(1000, 7);
assertTrue(survivor >= 1 && survivor <= 1000);
}

@Test
void testInvalidInputs() {
assertThrows(IllegalArgumentException.class, () -> Josephus.getJosephus(0, 3));
assertThrows(IllegalArgumentException.class, () -> Josephus.getJosephus(-5, 2));
assertThrows(IllegalArgumentException.class, () -> Josephus.getJosephus(5, 0));
assertThrows(IllegalArgumentException.class, () -> Josephus.getJosephus(5, -1));
}

@Test
void testPrintJosephus() {
// Capture System.out
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
PrintStream originalOut = System.out;
System.setOut(new PrintStream(outputStream));

try {
Josephus.printJosephus(7, 3); // known survivor 4
String output = outputStream.toString().trim();
// output should contain just the survivor number (since printJosephus prints only the number)
assertEquals("4", output);
} finally {
System.setOut(originalOut);
}
}

@Test
void testMainMethodContainsDemo() {
// Capture System.out
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
PrintStream originalOut = System.out;
System.setOut(new PrintStream(outputStream));

try {
Josephus.main(new String[] {});
String output = outputStream.toString();
assertTrue(output.contains("Josephus problem demo:"));
assertTrue(output.contains("Survivor (1-based position):"));
} finally {
System.setOut(originalOut);
}
}
}
Loading