mirror of
https://github.com/TheAlgorithms/Java.git
synced 2025-12-19 07:00:35 +08:00
refactor: Enhance docs, code, add tests in KeithNumber (#6748)
* refactor: Enhance docs, code, add tests in `KeithNumber` * Fix --------- Co-authored-by: a <alexanderklmn@gmail.com>
This commit is contained in:
@@ -4,57 +4,98 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Scanner;
|
||||
|
||||
final class KeithNumber {
|
||||
/**
|
||||
* A Keith number is an n-digit positive integer where the sequence formed by
|
||||
* starting with its digits and repeatedly adding the previous n terms,
|
||||
* eventually reaches the number itself.
|
||||
*
|
||||
* <p>
|
||||
* For example:
|
||||
* <ul>
|
||||
* <li>14 is a Keith number: 1, 4, 5, 9, 14</li>
|
||||
* <li>19 is a Keith number: 1, 9, 10, 19</li>
|
||||
* <li>28 is a Keith number: 2, 8, 10, 18, 28</li>
|
||||
* <li>197 is a Keith number: 1, 9, 7, 17, 33, 57, 107, 197</li>
|
||||
* </ul>
|
||||
*
|
||||
* @see <a href="https://en.wikipedia.org/wiki/Keith_number">Keith Number -
|
||||
* Wikipedia</a>
|
||||
* @see <a href="https://mathworld.wolfram.com/KeithNumber.html">Keith Number -
|
||||
* MathWorld</a>
|
||||
*/
|
||||
public final class KeithNumber {
|
||||
private KeithNumber() {
|
||||
}
|
||||
|
||||
// user-defined function that checks if the given number is Keith or not
|
||||
static boolean isKeith(int x) {
|
||||
// List stores all the digits of the X
|
||||
ArrayList<Integer> terms = new ArrayList<>();
|
||||
// n denotes the number of digits
|
||||
int temp = x;
|
||||
int n = 0;
|
||||
// executes until the condition becomes false
|
||||
while (temp > 0) {
|
||||
// determines the last digit of the number and add it to the List
|
||||
terms.add(temp % 10);
|
||||
// removes the last digit
|
||||
temp = temp / 10;
|
||||
// increments the number of digits (n) by 1
|
||||
n++;
|
||||
/**
|
||||
* Checks if a given number is a Keith number.
|
||||
*
|
||||
* <p>
|
||||
* The algorithm works as follows:
|
||||
* <ol>
|
||||
* <li>Extract all digits of the number and store them in a list</li>
|
||||
* <li>Generate subsequent terms by summing the last n digits</li>
|
||||
* <li>Continue until a term equals or exceeds the original number</li>
|
||||
* <li>If a term equals the number, it is a Keith number</li>
|
||||
* </ol>
|
||||
*
|
||||
* @param number the number to check (must be positive)
|
||||
* @return {@code true} if the number is a Keith number, {@code false} otherwise
|
||||
* @throws IllegalArgumentException if the number is not positive
|
||||
*/
|
||||
public static boolean isKeith(int number) {
|
||||
if (number <= 0) {
|
||||
throw new IllegalArgumentException("Number must be positive");
|
||||
}
|
||||
// reverse the List
|
||||
|
||||
// Extract digits and store them in the list
|
||||
ArrayList<Integer> terms = new ArrayList<>();
|
||||
int temp = number;
|
||||
int digitCount = 0;
|
||||
|
||||
while (temp > 0) {
|
||||
terms.add(temp % 10);
|
||||
temp = temp / 10;
|
||||
digitCount++;
|
||||
}
|
||||
|
||||
// Reverse the list to get digits in correct order
|
||||
Collections.reverse(terms);
|
||||
|
||||
// Generate subsequent terms in the sequence
|
||||
int nextTerm = 0;
|
||||
int i = n;
|
||||
// finds next term for the series
|
||||
// loop executes until the condition returns true
|
||||
while (nextTerm < x) {
|
||||
int currentIndex = digitCount;
|
||||
|
||||
while (nextTerm < number) {
|
||||
nextTerm = 0;
|
||||
// next term is the sum of previous n terms (it depends on number of digits the number
|
||||
// has)
|
||||
for (int j = 1; j <= n; j++) {
|
||||
nextTerm = nextTerm + terms.get(i - j);
|
||||
// Sum the last 'digitCount' terms
|
||||
for (int j = 1; j <= digitCount; j++) {
|
||||
nextTerm = nextTerm + terms.get(currentIndex - j);
|
||||
}
|
||||
terms.add(nextTerm);
|
||||
i++;
|
||||
currentIndex++;
|
||||
}
|
||||
// when the control comes out of the while loop, there will be two conditions:
|
||||
// either nextTerm will be equal to x or greater than x
|
||||
// if equal, the given number is Keith, else not
|
||||
return (nextTerm == x);
|
||||
|
||||
// Check if the generated term equals the original number
|
||||
return (nextTerm == number);
|
||||
}
|
||||
|
||||
// driver code
|
||||
/**
|
||||
* Main method for demonstrating Keith number detection.
|
||||
* Reads a number from standard input and checks if it is a Keith number.
|
||||
*
|
||||
* @param args command line arguments (not used)
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
Scanner in = new Scanner(System.in);
|
||||
int n = in.nextInt();
|
||||
if (isKeith(n)) {
|
||||
System.out.println("Yes, the given number is a Keith number.");
|
||||
Scanner scanner = new Scanner(System.in);
|
||||
System.out.print("Enter a positive integer: ");
|
||||
int number = scanner.nextInt();
|
||||
|
||||
if (isKeith(number)) {
|
||||
System.out.println("Yes, " + number + " is a Keith number.");
|
||||
} else {
|
||||
System.out.println("No, the given number is not a Keith number.");
|
||||
System.out.println("No, " + number + " is not a Keith number.");
|
||||
}
|
||||
in.close();
|
||||
scanner.close();
|
||||
}
|
||||
}
|
||||
|
||||
153
src/test/java/com/thealgorithms/maths/KeithNumberTest.java
Normal file
153
src/test/java/com/thealgorithms/maths/KeithNumberTest.java
Normal file
@@ -0,0 +1,153 @@
|
||||
package com.thealgorithms.maths;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* Test cases for {@link KeithNumber}.
|
||||
*/
|
||||
class KeithNumberTest {
|
||||
|
||||
/**
|
||||
* Tests single-digit Keith numbers.
|
||||
* All single-digit numbers (1-9) are Keith numbers by definition.
|
||||
*/
|
||||
@Test
|
||||
void testSingleDigitKeithNumbers() {
|
||||
assertTrue(KeithNumber.isKeith(1));
|
||||
assertTrue(KeithNumber.isKeith(2));
|
||||
assertTrue(KeithNumber.isKeith(3));
|
||||
assertTrue(KeithNumber.isKeith(4));
|
||||
assertTrue(KeithNumber.isKeith(5));
|
||||
assertTrue(KeithNumber.isKeith(6));
|
||||
assertTrue(KeithNumber.isKeith(7));
|
||||
assertTrue(KeithNumber.isKeith(8));
|
||||
assertTrue(KeithNumber.isKeith(9));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests two-digit Keith numbers.
|
||||
* Known two-digit Keith numbers: 14, 19, 28, 47, 61, 75.
|
||||
*/
|
||||
@Test
|
||||
void testTwoDigitKeithNumbers() {
|
||||
assertTrue(KeithNumber.isKeith(14));
|
||||
assertTrue(KeithNumber.isKeith(19));
|
||||
assertTrue(KeithNumber.isKeith(28));
|
||||
assertTrue(KeithNumber.isKeith(47));
|
||||
assertTrue(KeithNumber.isKeith(61));
|
||||
assertTrue(KeithNumber.isKeith(75));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests three-digit Keith numbers.
|
||||
* Known three-digit Keith numbers: 197, 742.
|
||||
*/
|
||||
@Test
|
||||
void testThreeDigitKeithNumbers() {
|
||||
assertTrue(KeithNumber.isKeith(197));
|
||||
assertTrue(KeithNumber.isKeith(742));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests four-digit Keith numbers.
|
||||
* Known four-digit Keith numbers: 1104, 1537, 2208, 2580, 3684, 4788, 7385,
|
||||
* 7647, 7909.
|
||||
*/
|
||||
@Test
|
||||
void testFourDigitKeithNumbers() {
|
||||
assertTrue(KeithNumber.isKeith(1104));
|
||||
assertTrue(KeithNumber.isKeith(1537));
|
||||
assertTrue(KeithNumber.isKeith(2208));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests two-digit non-Keith numbers.
|
||||
*/
|
||||
@Test
|
||||
void testTwoDigitNonKeithNumbers() {
|
||||
assertFalse(KeithNumber.isKeith(10));
|
||||
assertFalse(KeithNumber.isKeith(11));
|
||||
assertFalse(KeithNumber.isKeith(12));
|
||||
assertFalse(KeithNumber.isKeith(13));
|
||||
assertFalse(KeithNumber.isKeith(15));
|
||||
assertFalse(KeithNumber.isKeith(20));
|
||||
assertFalse(KeithNumber.isKeith(30));
|
||||
assertFalse(KeithNumber.isKeith(50));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests three-digit non-Keith numbers.
|
||||
*/
|
||||
@Test
|
||||
void testThreeDigitNonKeithNumbers() {
|
||||
assertFalse(KeithNumber.isKeith(100));
|
||||
assertFalse(KeithNumber.isKeith(123));
|
||||
assertFalse(KeithNumber.isKeith(196));
|
||||
assertFalse(KeithNumber.isKeith(198));
|
||||
assertFalse(KeithNumber.isKeith(456));
|
||||
assertFalse(KeithNumber.isKeith(741));
|
||||
assertFalse(KeithNumber.isKeith(743));
|
||||
assertFalse(KeithNumber.isKeith(999));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests validation for edge case 14 in detail.
|
||||
* 14 is a Keith number: 1, 4, 5 (1+4), 9 (4+5), 14 (5+9).
|
||||
*/
|
||||
@Test
|
||||
void testKeithNumber14() {
|
||||
assertTrue(KeithNumber.isKeith(14));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests validation for edge case 197 in detail.
|
||||
* 197 is a Keith number: 1, 9, 7, 17, 33, 57, 107, 197.
|
||||
*/
|
||||
@Test
|
||||
void testKeithNumber197() {
|
||||
assertTrue(KeithNumber.isKeith(197));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that zero throws an IllegalArgumentException.
|
||||
*/
|
||||
@Test
|
||||
void testZeroThrowsException() {
|
||||
assertThrows(IllegalArgumentException.class, () -> KeithNumber.isKeith(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that negative numbers throw an IllegalArgumentException.
|
||||
*/
|
||||
@Test
|
||||
void testNegativeNumbersThrowException() {
|
||||
assertThrows(IllegalArgumentException.class, () -> KeithNumber.isKeith(-1));
|
||||
assertThrows(IllegalArgumentException.class, () -> KeithNumber.isKeith(-14));
|
||||
assertThrows(IllegalArgumentException.class, () -> KeithNumber.isKeith(-100));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests various edge cases with larger numbers.
|
||||
*/
|
||||
@Test
|
||||
void testLargerNumbers() {
|
||||
assertTrue(KeithNumber.isKeith(2208));
|
||||
assertFalse(KeithNumber.isKeith(2207));
|
||||
assertFalse(KeithNumber.isKeith(2209));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the expected behavior with all two-digit Keith numbers.
|
||||
*/
|
||||
@Test
|
||||
void testAllKnownTwoDigitKeithNumbers() {
|
||||
int[] knownKeithNumbers = {14, 19, 28, 47, 61, 75};
|
||||
for (int number : knownKeithNumbers) {
|
||||
assertTrue(KeithNumber.isKeith(number), "Expected " + number + " to be a Keith number");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user