From 441359b756374993ec8ce8d48e9e3b77eb88758a Mon Sep 17 00:00:00 2001 From: trishajp38-source Date: Sat, 29 Nov 2025 21:05:09 +0530 Subject: [PATCH 1/5] refactor: remove duplicate factorial implementations and add improved iterative factorial with full test suite --- .../divideandconquer/Factorial.java | 33 ++++++++ .../com/thealgorithms/maths/Factorial.java | 23 ------ .../maths/FactorialRecursion.java | 18 ----- .../divideandconquer/FactorialTest.java | 78 +++++++++++++++++++ .../maths/FactorialRecursionTest.java | 27 ------- .../thealgorithms/maths/FactorialTest.java | 24 ------ 6 files changed, 111 insertions(+), 92 deletions(-) create mode 100644 src/main/java/com/thealgorithms/divideandconquer/Factorial.java delete mode 100644 src/main/java/com/thealgorithms/maths/Factorial.java delete mode 100644 src/main/java/com/thealgorithms/maths/FactorialRecursion.java create mode 100644 src/test/java/com/thealgorithms/divideandconquer/FactorialTest.java delete mode 100644 src/test/java/com/thealgorithms/maths/FactorialRecursionTest.java delete mode 100644 src/test/java/com/thealgorithms/maths/FactorialTest.java diff --git a/src/main/java/com/thealgorithms/divideandconquer/Factorial.java b/src/main/java/com/thealgorithms/divideandconquer/Factorial.java new file mode 100644 index 000000000000..4a5a84a97be1 --- /dev/null +++ b/src/main/java/com/thealgorithms/divideandconquer/Factorial.java @@ -0,0 +1,33 @@ +package com.thealgorithms.divideandconquer; +/** + * Computes the factorial of a non-negative integer using an iterative + * approach to avoid recursion overhead and stack overflow risks. + * + *

This implementation improves upon the original recursive version by + * eliminating deep recursion, reducing space complexity from O(n) to O(1), + * and improving runtime efficiency on the JVM. + * + *

Time Complexity: O(n) + *
Space Complexity: O(1) + */ +public class Factorial { + /** + * Returns the factorial of the given non-negative number. + * + * @param n the number to compute factorial for + * @return factorial of n (n!) + * @throws IllegalArgumentException if n is negative + */ + public static long factorial(long n) { + if (n < 0) { + throw new IllegalArgumentException("Negative input not allowed"); + } + + long result = 1; + for (long i = 2; i <= n; i++) { + result *= i; + } + + return result; + } +} diff --git a/src/main/java/com/thealgorithms/maths/Factorial.java b/src/main/java/com/thealgorithms/maths/Factorial.java deleted file mode 100644 index 511cc1f84f05..000000000000 --- a/src/main/java/com/thealgorithms/maths/Factorial.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.thealgorithms.maths; - -public final class Factorial { - private Factorial() { - } - - /** - * Calculate factorial N using iteration - * - * @param n the number - * @return the factorial of {@code n} - */ - public static long factorial(int n) { - if (n < 0) { - throw new IllegalArgumentException("Input number cannot be negative"); - } - long factorial = 1; - for (int i = 1; i <= n; ++i) { - factorial *= i; - } - return factorial; - } -} diff --git a/src/main/java/com/thealgorithms/maths/FactorialRecursion.java b/src/main/java/com/thealgorithms/maths/FactorialRecursion.java deleted file mode 100644 index d9bafd1e39e9..000000000000 --- a/src/main/java/com/thealgorithms/maths/FactorialRecursion.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.thealgorithms.maths; - -public final class FactorialRecursion { - private FactorialRecursion() { - } - /** - * Recursive FactorialRecursion Method - * - * @param n The number to factorial - * @return The factorial of the number - */ - public static long factorial(int n) { - if (n < 0) { - throw new IllegalArgumentException("number is negative"); - } - return n == 0 || n == 1 ? 1 : n * factorial(n - 1); - } -} diff --git a/src/test/java/com/thealgorithms/divideandconquer/FactorialTest.java b/src/test/java/com/thealgorithms/divideandconquer/FactorialTest.java new file mode 100644 index 000000000000..a6cd6b282f9f --- /dev/null +++ b/src/test/java/com/thealgorithms/divideandconquer/FactorialTest.java @@ -0,0 +1,78 @@ +package com.thealgorithms.divideandconquer; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; + + +public class FactorialTest { + // -------------------------------------------------------- + // SECTION 1: Basic Correctness Tests + // -------------------------------------------------------- + + @Test + void testFactorialOfFive() { + assertEquals(120, Factorial.factorial(5)); + } + + @Test + void testFactorialOfZero() { + assertEquals(1, Factorial.factorial(0)); + } + + @Test + void testNegativeInputThrowsException() { + assertThrows(IllegalArgumentException.class, () -> { + Factorial.factorial(-5); + }); + } + + // -------------------------------------------------------- + // SECTION 2: Analysis-Oriented Test (Performance Awareness) + // -------------------------------------------------------- + + @Test + void testLargeInputPerformance() { + long start = System.currentTimeMillis(); + long result = Factorial.factorial(15); + long end = System.currentTimeMillis(); + + assertEquals(1307674368000L, result); + assertTrue((end - start) < 50, "Factorial(15) took too long to compute"); + } + + // -------------------------------------------------------- + // SECTION 3: Algorithmic Improvement Demonstration + // -------------------------------------------------------- + + /** + * Local copy of the original recursive implementation + * used only for comparing performance inside the test. + */ + private long recursiveFactorial(long n) { + if (n < 0) throw new IllegalArgumentException("Negative input not allowed"); + if (n == 0 || n == 1) return 1; + return n * recursiveFactorial(n - 1); + } + + @Test + void testIterativeFasterThanRecursive() { + long n = 18; + + long startRec = System.nanoTime(); + long recResult = recursiveFactorial(n); + long endRec = System.nanoTime(); + + long startIter = System.nanoTime(); + long iterResult = Factorial.factorial(n); + long endIter = System.nanoTime(); + + assertEquals(recResult, iterResult); + assertTrue(endIter - startIter < endRec - startRec, + "Iterative version should outperform recursive version"); + } + + @Test + void testIterativeHandlesLargerInputsSafely() { + assertDoesNotThrow(() -> Factorial.factorial(20)); + } +} diff --git a/src/test/java/com/thealgorithms/maths/FactorialRecursionTest.java b/src/test/java/com/thealgorithms/maths/FactorialRecursionTest.java deleted file mode 100644 index db18b46356b4..000000000000 --- a/src/test/java/com/thealgorithms/maths/FactorialRecursionTest.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.thealgorithms.maths; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; - -import java.util.stream.Stream; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; - -public class FactorialRecursionTest { - @ParameterizedTest - @MethodSource("inputStream") - void testFactorialRecursion(long expected, int number) { - assertEquals(expected, FactorialRecursion.factorial(number)); - } - - private static Stream inputStream() { - return Stream.of(Arguments.of(1, 0), Arguments.of(1, 1), Arguments.of(2, 2), Arguments.of(6, 3), Arguments.of(120, 5)); - } - - @Test - void testThrowsForNegativeInput() { - assertThrows(IllegalArgumentException.class, () -> FactorialRecursion.factorial(-1)); - } -} diff --git a/src/test/java/com/thealgorithms/maths/FactorialTest.java b/src/test/java/com/thealgorithms/maths/FactorialTest.java deleted file mode 100644 index b38dc45589ee..000000000000 --- a/src/test/java/com/thealgorithms/maths/FactorialTest.java +++ /dev/null @@ -1,24 +0,0 @@ -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; - -public class FactorialTest { - private static final String EXCEPTION_MESSAGE = "Input number cannot be negative"; - - @Test - public void testWhenInvalidInoutProvidedShouldThrowException() { - IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> Factorial.factorial(-1)); - assertEquals(exception.getMessage(), EXCEPTION_MESSAGE); - } - - @Test - public void testCorrectFactorialCalculation() { - assertEquals(1, Factorial.factorial(0)); - assertEquals(1, Factorial.factorial(1)); - assertEquals(120, Factorial.factorial(5)); - assertEquals(3628800, Factorial.factorial(10)); - } -} From 3d0e6d0f207e003b9f387e9a9c23c1bc69a35be3 Mon Sep 17 00:00:00 2001 From: trishajp38-source Date: Sat, 29 Nov 2025 21:29:00 +0530 Subject: [PATCH 2/5] style: fix checkstyle violations and apply formatting --- .../divideandconquer/Factorial.java | 3 ++ .../divideandconquer/FactorialTest.java | 36 +++++++++++-------- 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/thealgorithms/divideandconquer/Factorial.java b/src/main/java/com/thealgorithms/divideandconquer/Factorial.java index 4a5a84a97be1..775a889649bb 100644 --- a/src/main/java/com/thealgorithms/divideandconquer/Factorial.java +++ b/src/main/java/com/thealgorithms/divideandconquer/Factorial.java @@ -18,6 +18,9 @@ public class Factorial { * @return factorial of n (n!) * @throws IllegalArgumentException if n is negative */ + private Factorial() { + // Utility class + } public static long factorial(long n) { if (n < 0) { throw new IllegalArgumentException("Negative input not allowed"); diff --git a/src/test/java/com/thealgorithms/divideandconquer/FactorialTest.java b/src/test/java/com/thealgorithms/divideandconquer/FactorialTest.java index a6cd6b282f9f..8dc7b2ee0f3b 100644 --- a/src/test/java/com/thealgorithms/divideandconquer/FactorialTest.java +++ b/src/test/java/com/thealgorithms/divideandconquer/FactorialTest.java @@ -1,11 +1,15 @@ package com.thealgorithms.divideandconquer; -import static org.junit.jupiter.api.Assertions.*; -import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +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 org.junit.jupiter.api.Test; public class FactorialTest { - // -------------------------------------------------------- + + // -------------------------------------------------------- // SECTION 1: Basic Correctness Tests // -------------------------------------------------------- @@ -21,9 +25,7 @@ void testFactorialOfZero() { @Test void testNegativeInputThrowsException() { - assertThrows(IllegalArgumentException.class, () -> { - Factorial.factorial(-5); - }); + assertThrows(IllegalArgumentException.class, () -> Factorial.factorial(-5)); } // -------------------------------------------------------- @@ -45,16 +47,21 @@ void testLargeInputPerformance() { // -------------------------------------------------------- /** - * Local copy of the original recursive implementation - * used only for comparing performance inside the test. + * Local copy of the original recursive implementation used only for comparing performance inside + * the test. */ private long recursiveFactorial(long n) { - if (n < 0) throw new IllegalArgumentException("Negative input not allowed"); - if (n == 0 || n == 1) return 1; - return n * recursiveFactorial(n - 1); + if (n < 0) { + throw new IllegalArgumentException("Negative input not allowed"); + } + if (n == 0 || n == 1) { + return 1; + } else { + return n * recursiveFactorial(n - 1); + } } - @Test + @Test void testIterativeFasterThanRecursive() { long n = 18; @@ -67,11 +74,12 @@ void testIterativeFasterThanRecursive() { long endIter = System.nanoTime(); assertEquals(recResult, iterResult); - assertTrue(endIter - startIter < endRec - startRec, + assertTrue( + endIter - startIter < endRec - startRec, "Iterative version should outperform recursive version"); } - @Test + @Test void testIterativeHandlesLargerInputsSafely() { assertDoesNotThrow(() -> Factorial.factorial(20)); } From c2b8353edbb52a706b45c02e922ec578c50bda79 Mon Sep 17 00:00:00 2001 From: trishajp38-source Date: Sat, 29 Nov 2025 21:35:42 +0530 Subject: [PATCH 3/5] style: mark Factorial class as final to satisfy Checkstyle --- .../thealgorithms/divideandconquer/Factorial.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/thealgorithms/divideandconquer/Factorial.java b/src/main/java/com/thealgorithms/divideandconquer/Factorial.java index 775a889649bb..934d96bf40d4 100644 --- a/src/main/java/com/thealgorithms/divideandconquer/Factorial.java +++ b/src/main/java/com/thealgorithms/divideandconquer/Factorial.java @@ -1,4 +1,5 @@ package com.thealgorithms.divideandconquer; + /** * Computes the factorial of a non-negative integer using an iterative * approach to avoid recursion overhead and stack overflow risks. @@ -10,7 +11,12 @@ *

Time Complexity: O(n) *
Space Complexity: O(1) */ -public class Factorial { +public final class Factorial { + + private Factorial() { + // Utility class + } + /** * Returns the factorial of the given non-negative number. * @@ -18,9 +24,6 @@ public class Factorial { * @return factorial of n (n!) * @throws IllegalArgumentException if n is negative */ - private Factorial() { - // Utility class - } public static long factorial(long n) { if (n < 0) { throw new IllegalArgumentException("Negative input not allowed"); @@ -34,3 +37,4 @@ public static long factorial(long n) { return result; } } + From c2489eb8406ec38491cd86856a72dc1d512d47a5 Mon Sep 17 00:00:00 2001 From: trishajp38-source Date: Sat, 29 Nov 2025 21:43:35 +0530 Subject: [PATCH 4/5] style: apply clang-format compliant formatting --- src/main/java/com/thealgorithms/divideandconquer/Factorial.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/com/thealgorithms/divideandconquer/Factorial.java b/src/main/java/com/thealgorithms/divideandconquer/Factorial.java index 934d96bf40d4..17f01326dc4b 100644 --- a/src/main/java/com/thealgorithms/divideandconquer/Factorial.java +++ b/src/main/java/com/thealgorithms/divideandconquer/Factorial.java @@ -38,3 +38,4 @@ public static long factorial(long n) { } } + From b7942fb7764d83c1783acba870c0b05468b9d024 Mon Sep 17 00:00:00 2001 From: trishajp38-source Date: Sat, 29 Nov 2025 22:01:38 +0530 Subject: [PATCH 5/5] style: apply clang-format --- .../thealgorithms/divideandconquer/Factorial.java | 2 -- .../divideandconquer/FactorialTest.java | 14 +++++--------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/thealgorithms/divideandconquer/Factorial.java b/src/main/java/com/thealgorithms/divideandconquer/Factorial.java index 17f01326dc4b..ec9db6f2ff5d 100644 --- a/src/main/java/com/thealgorithms/divideandconquer/Factorial.java +++ b/src/main/java/com/thealgorithms/divideandconquer/Factorial.java @@ -37,5 +37,3 @@ public static long factorial(long n) { return result; } } - - diff --git a/src/test/java/com/thealgorithms/divideandconquer/FactorialTest.java b/src/test/java/com/thealgorithms/divideandconquer/FactorialTest.java index 8dc7b2ee0f3b..50d63c5e970b 100644 --- a/src/test/java/com/thealgorithms/divideandconquer/FactorialTest.java +++ b/src/test/java/com/thealgorithms/divideandconquer/FactorialTest.java @@ -8,7 +8,6 @@ import org.junit.jupiter.api.Test; public class FactorialTest { - // -------------------------------------------------------- // SECTION 1: Basic Correctness Tests // -------------------------------------------------------- @@ -25,7 +24,7 @@ void testFactorialOfZero() { @Test void testNegativeInputThrowsException() { - assertThrows(IllegalArgumentException.class, () -> Factorial.factorial(-5)); + assertThrows(IllegalArgumentException.class, () -> { Factorial.factorial(-5); }); } // -------------------------------------------------------- @@ -47,8 +46,8 @@ void testLargeInputPerformance() { // -------------------------------------------------------- /** - * Local copy of the original recursive implementation used only for comparing performance inside - * the test. + * Local copy of the original recursive implementation + * used only for comparing performance inside the test. */ private long recursiveFactorial(long n) { if (n < 0) { @@ -56,9 +55,8 @@ private long recursiveFactorial(long n) { } if (n == 0 || n == 1) { return 1; - } else { - return n * recursiveFactorial(n - 1); } + return n * recursiveFactorial(n - 1); } @Test @@ -74,9 +72,7 @@ void testIterativeFasterThanRecursive() { long endIter = System.nanoTime(); assertEquals(recResult, iterResult); - assertTrue( - endIter - startIter < endRec - startRec, - "Iterative version should outperform recursive version"); + assertTrue(endIter - startIter < endRec - startRec, "Iterative version should outperform recursive version"); } @Test