diff --git a/src/main/java/com/thealgorithms/recursion/Josephus.java b/src/main/java/com/thealgorithms/recursion/Josephus.java new file mode 100644 index 000000000000..03ce86480a9d --- /dev/null +++ b/src/main/java/com/thealgorithms/recursion/Josephus.java @@ -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 Josephus problem + */ +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); + } +} diff --git a/src/test/java/com/thealgorithms/recursion/JosephusTest.java b/src/test/java/com/thealgorithms/recursion/JosephusTest.java new file mode 100644 index 000000000000..dc9616eb646c --- /dev/null +++ b/src/test/java/com/thealgorithms/recursion/JosephusTest.java @@ -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); + } + } +}