refactor: Enhance docs, code, add tests in LeonardoNumber (#6743)

This commit is contained in:
Hardik Pawar
2025-10-13 13:12:10 +05:30
committed by GitHub
parent 883a050162
commit f30d10117d
2 changed files with 216 additions and 20 deletions

View File

@@ -1,25 +1,79 @@
package com.thealgorithms.maths;
/**
* https://en.wikipedia.org/wiki/Leonardo_number
* Utility class for calculating Leonardo Numbers.
* <p>
* Leonardo numbers are a sequence of numbers defined by the recurrence:
* L(n) = L(n-1) + L(n-2) + 1, with L(0) = 1 and L(1) = 1
* <p>
* The sequence begins: 1, 1, 3, 5, 9, 15, 25, 41, 67, 109, 177, ...
* <p>
* This class provides both a recursive implementation and an optimized
* iterative
* implementation for calculating Leonardo numbers.
*
* @see <a href="https://en.wikipedia.org/wiki/Leonardo_number">Leonardo Number
* - Wikipedia</a>
* @see <a href="https://oeis.org/A001595">OEIS A001595</a>
*/
public final class LeonardoNumber {
private LeonardoNumber() {
}
/**
* Calculate nth Leonardo Number (1, 1, 3, 5, 9, 15, 25, 41, 67, 109, 177, ...)
* Calculates the nth Leonardo Number using recursion.
* <p>
* Time Complexity: O(2^n) - exponential due to repeated calculations
* Space Complexity: O(n) - due to recursion stack
* <p>
* Note: This method is not recommended for large values of n due to exponential
* time complexity.
* Consider using {@link #leonardoNumberIterative(int)} for better performance.
*
* @param n the index of Leonardo Number to calculate
* @return nth number of Leonardo sequences
* @param n the index of the Leonardo Number to calculate (must be non-negative)
* @return the nth Leonardo Number
* @throws IllegalArgumentException if n is negative
*/
public static int leonardoNumber(int n) {
if (n < 0) {
throw new ArithmeticException();
throw new IllegalArgumentException("Input must be non-negative. Received: " + n);
}
if (n == 0 || n == 1) {
return 1;
}
return (leonardoNumber(n - 1) + leonardoNumber(n - 2) + 1);
return leonardoNumber(n - 1) + leonardoNumber(n - 2) + 1;
}
/**
* Calculates the nth Leonardo Number using an iterative approach.
* <p>
* This method provides better performance than the recursive version for large
* values of n.
* <p>
* Time Complexity: O(n)
* Space Complexity: O(1)
*
* @param n the index of the Leonardo Number to calculate (must be non-negative)
* @return the nth Leonardo Number
* @throws IllegalArgumentException if n is negative
*/
public static int leonardoNumberIterative(int n) {
if (n < 0) {
throw new IllegalArgumentException("Input must be non-negative. Received: " + n);
}
if (n == 0 || n == 1) {
return 1;
}
int previous = 1;
int current = 1;
for (int i = 2; i <= n; i++) {
int next = current + previous + 1;
previous = current;
current = next;
}
return current;
}
}

View File

@@ -1,29 +1,171 @@
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.Assertions;
import org.junit.jupiter.api.Test;
public class LeonardoNumberTest {
/**
* Test cases for {@link LeonardoNumber} class.
* <p>
* Tests both recursive and iterative implementations with various input values
* including edge cases and boundary conditions.
*/
class LeonardoNumberTest {
// Tests for recursive implementation
@Test
void leonardoNumberNegative() {
assertThrows(ArithmeticException.class, () -> LeonardoNumber.leonardoNumber(-1));
void testLeonardoNumberNegative() {
Assertions.assertThrows(IllegalArgumentException.class, () -> LeonardoNumber.leonardoNumber(-1));
}
@Test
void leonardoNumberZero() {
assertEquals(1, LeonardoNumber.leonardoNumber(0));
void testLeonardoNumberNegativeLarge() {
Assertions.assertThrows(IllegalArgumentException.class, () -> LeonardoNumber.leonardoNumber(-100));
}
@Test
void leonardoNumberOne() {
assertEquals(1, LeonardoNumber.leonardoNumber(1));
void testLeonardoNumberZero() {
Assertions.assertEquals(1, LeonardoNumber.leonardoNumber(0));
}
@Test
void leonardoNumberFive() {
assertEquals(15, LeonardoNumber.leonardoNumber(5));
void testLeonardoNumberOne() {
Assertions.assertEquals(1, LeonardoNumber.leonardoNumber(1));
}
@Test
void leonardoNumberTwenty() {
assertEquals(21891, LeonardoNumber.leonardoNumber(20));
void testLeonardoNumberTwo() {
Assertions.assertEquals(3, LeonardoNumber.leonardoNumber(2));
}
@Test
void testLeonardoNumberThree() {
Assertions.assertEquals(5, LeonardoNumber.leonardoNumber(3));
}
@Test
void testLeonardoNumberFour() {
Assertions.assertEquals(9, LeonardoNumber.leonardoNumber(4));
}
@Test
void testLeonardoNumberFive() {
Assertions.assertEquals(15, LeonardoNumber.leonardoNumber(5));
}
@Test
void testLeonardoNumberSix() {
Assertions.assertEquals(25, LeonardoNumber.leonardoNumber(6));
}
@Test
void testLeonardoNumberSeven() {
Assertions.assertEquals(41, LeonardoNumber.leonardoNumber(7));
}
@Test
void testLeonardoNumberEight() {
Assertions.assertEquals(67, LeonardoNumber.leonardoNumber(8));
}
@Test
void testLeonardoNumberTen() {
Assertions.assertEquals(177, LeonardoNumber.leonardoNumber(10));
}
@Test
void testLeonardoNumberFifteen() {
Assertions.assertEquals(1973, LeonardoNumber.leonardoNumber(15));
}
@Test
void testLeonardoNumberTwenty() {
Assertions.assertEquals(21891, LeonardoNumber.leonardoNumber(20));
}
// Tests for iterative implementation
@Test
void testLeonardoNumberIterativeNegative() {
Assertions.assertThrows(IllegalArgumentException.class, () -> LeonardoNumber.leonardoNumberIterative(-1));
}
@Test
void testLeonardoNumberIterativeNegativeLarge() {
Assertions.assertThrows(IllegalArgumentException.class, () -> LeonardoNumber.leonardoNumberIterative(-50));
}
@Test
void testLeonardoNumberIterativeZero() {
Assertions.assertEquals(1, LeonardoNumber.leonardoNumberIterative(0));
}
@Test
void testLeonardoNumberIterativeOne() {
Assertions.assertEquals(1, LeonardoNumber.leonardoNumberIterative(1));
}
@Test
void testLeonardoNumberIterativeTwo() {
Assertions.assertEquals(3, LeonardoNumber.leonardoNumberIterative(2));
}
@Test
void testLeonardoNumberIterativeThree() {
Assertions.assertEquals(5, LeonardoNumber.leonardoNumberIterative(3));
}
@Test
void testLeonardoNumberIterativeFour() {
Assertions.assertEquals(9, LeonardoNumber.leonardoNumberIterative(4));
}
@Test
void testLeonardoNumberIterativeFive() {
Assertions.assertEquals(15, LeonardoNumber.leonardoNumberIterative(5));
}
@Test
void testLeonardoNumberIterativeSix() {
Assertions.assertEquals(25, LeonardoNumber.leonardoNumberIterative(6));
}
@Test
void testLeonardoNumberIterativeSeven() {
Assertions.assertEquals(41, LeonardoNumber.leonardoNumberIterative(7));
}
@Test
void testLeonardoNumberIterativeEight() {
Assertions.assertEquals(67, LeonardoNumber.leonardoNumberIterative(8));
}
@Test
void testLeonardoNumberIterativeTen() {
Assertions.assertEquals(177, LeonardoNumber.leonardoNumberIterative(10));
}
@Test
void testLeonardoNumberIterativeFifteen() {
Assertions.assertEquals(1973, LeonardoNumber.leonardoNumberIterative(15));
}
@Test
void testLeonardoNumberIterativeTwenty() {
Assertions.assertEquals(21891, LeonardoNumber.leonardoNumberIterative(20));
}
@Test
void testLeonardoNumberIterativeTwentyFive() {
Assertions.assertEquals(242785, LeonardoNumber.leonardoNumberIterative(25));
}
// Consistency tests between recursive and iterative implementations
@Test
void testConsistencyBetweenImplementations() {
for (int i = 0; i <= 15; i++) {
Assertions.assertEquals(LeonardoNumber.leonardoNumber(i), LeonardoNumber.leonardoNumberIterative(i), "Mismatch at index " + i);
}
}
}