diff --git a/src/main/java/com/thealgorithms/maths/BellNumbers.java b/src/main/java/com/thealgorithms/maths/BellNumbers.java new file mode 100644 index 000000000000..d4dc1014f48b --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/BellNumbers.java @@ -0,0 +1,59 @@ +package com.thealgorithms.maths; + +/** + * The Bell numbers count the number of partitions of a set. + * The n-th Bell number is the number of ways a set of n elements can be partitioned + * into nonempty subsets. + * + *

+ * This implementation uses the Bell Triangle (Aitken's array) method. + * Time Complexity: O(n^2) + * Space Complexity: O(n^2) + *

+ * + * @author Chahat Sandhu, singhc7 + * @see Bell Number (Wikipedia) + */ +public final class BellNumbers { + + private BellNumbers() { + } + + /** + * Calculates the n-th Bell number using the Bell Triangle. + * + * @param n the index of the Bell number (must be non-negative) + * @return the n-th Bell number + * @throws IllegalArgumentException if n is negative or n > 25 + */ + public static long compute(int n) { + if (n < 0) { + throw new IllegalArgumentException("n must be non-negative"); + } + if (n == 0) { + return 1; + } + if (n > 25) { + throw new IllegalArgumentException("n must be <= 25. For larger n, use BigInteger implementation."); + } + + // We use a 2D array to visualize the Bell Triangle + long[][] bellTriangle = new long[n + 1][n + 1]; + + // Base case: The triangle starts with 1 + bellTriangle[0][0] = 1; + + for (int i = 1; i <= n; i++) { + // Rule 1: The first number in a new row is the LAST number of the previous row + bellTriangle[i][0] = bellTriangle[i - 1][i - 1]; + + // Rule 2: Fill the rest of the row by adding the previous neighbor and the upper-left neighbor + for (int j = 1; j <= i; j++) { + bellTriangle[i][j] = bellTriangle[i][j - 1] + bellTriangle[i - 1][j - 1]; + } + } + + // The Bell number B_n is the first number in the n-th row + return bellTriangle[n][0]; + } +} diff --git a/src/test/java/com/thealgorithms/maths/BellNumbersTest.java b/src/test/java/com/thealgorithms/maths/BellNumbersTest.java new file mode 100644 index 000000000000..8dd83cf0f7a9 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/BellNumbersTest.java @@ -0,0 +1,53 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +class BellNumbersTest { + + @Test + void testStandardCases() { + // Base cases and small numbers + assertEquals(1, BellNumbers.compute(0)); + assertEquals(1, BellNumbers.compute(1)); + assertEquals(2, BellNumbers.compute(2)); + assertEquals(5, BellNumbers.compute(3)); + assertEquals(15, BellNumbers.compute(4)); + assertEquals(52, BellNumbers.compute(5)); + } + + @Test + void testMediumNumber() { + // B10 = 115,975 + assertEquals(115975, BellNumbers.compute(10)); + // B15 = 1,382,958,545 + assertEquals(1382958545L, BellNumbers.compute(15)); + } + + @Test + void testLargeNumber() { + // B20 = 51,724,158,235,372 + // We use the 'L' suffix to tell Java this is a long literal + assertEquals(51724158235372L, BellNumbers.compute(20)); + } + + @Test + void testMaxLongCapacity() { + // B25 is the largest Bell number that fits in a Java long (signed 64-bit) + // B25 = 4,638,590,332,229,999,353 + assertEquals(4638590332229999353L, BellNumbers.compute(25)); + } + + @Test + void testNegativeInput() { + assertThrows(IllegalArgumentException.class, () -> BellNumbers.compute(-1)); + } + + @Test + void testOverflowProtection() { + // We expect an exception if the user asks for the impossible + assertThrows(IllegalArgumentException.class, () -> BellNumbers.compute(26)); + } +}