Files
Python/maths/special_numbers/kaprekar_constant.py
Michael HauseggerTheRealHauiChristian Clausspre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>pre-commit-ci[bot] <pre-commit-ci[bot]@users.noreply.github.com>
7691204a88 Added some Unit Tests to increase test code coverage (#13903)
* Added some Unit Tests to increase test code coverage

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* updating DIRECTORY.md

---------

Co-authored-by: TheRealHaui <michael.hausegger@hausegger.tech>
Co-authored-by: Christian Clauss <cclauss@me.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <pre-commit-ci[bot]@users.noreply.github.com>
2026-09-05 23:35:55 +02:00

207 lines
6.2 KiB
Python

"""
Kaprekar's Constant (6174) - also known as Kaprekar's Routine
Kaprekar's constant is a special number discovered by Indian mathematician
D. R. Kaprekar in 1949. It is notable for the following property:
1. Take any four-digit number with at least two different digits (leading zeros allowed)
2. Arrange the digits in descending order to form the largest possible number
3. Arrange the digits in ascending order to form the smallest possible number
4. Subtract the smaller number from the larger number
5. Repeat the process with the result
This process will always reach 6174 in at most 7 iterations, and once 6174 is
reached, the process will continue to yield 6174.
Example:
3524 -> 5432 - 2345 = 3087
3087 -> 8730 - 0378 = 8352
8352 -> 8532 - 2358 = 6174
6174 -> 7641 - 1467 = 6174 (repeats)
Reference: https://en.wikipedia.org/wiki/6174_(number)
OEIS: https://oeis.org/A099009
"""
def kaprekar_routine(number: int) -> int:
"""
Perform one iteration of Kaprekar's routine.
Args:
number: A 4-digit number (0-9999)
Returns:
The result of one Kaprekar iteration
>>> kaprekar_routine(3524)
3087
>>> kaprekar_routine(3087)
8352
>>> kaprekar_routine(8352)
6174
>>> kaprekar_routine(6174)
6174
>>> kaprekar_routine(1)
999
>>> kaprekar_routine(1111)
0
"""
if not isinstance(number, int) or number < 0 or number > 9999:
msg = f"Input must be a non-negative integer between 0 and 9999, got {number}"
raise ValueError(msg)
# Convert to 4-digit string with leading zeros
digits = str(number).zfill(4)
# Check if all digits are the same (repdigit)
if len(set(digits)) == 1:
return 0
# Sort digits in descending and ascending order
descending = int("".join(sorted(digits, reverse=True)))
ascending = int("".join(sorted(digits)))
return descending - ascending
def kaprekar_constant(number: int, max_iterations: int = 7) -> tuple[int, list[int]]:
"""
Apply Kaprekar's routine until reaching the constant 6174 or max iterations.
Args:
number: A 4-digit number (0-9999)
max_iterations: Maximum number of iterations to perform (default: 7)
Returns:
A tuple containing:
- The number of iterations taken to reach 6174 (or -1 if not reached)
- A list of all intermediate results
>>> kaprekar_constant(3524)
(3, [3524, 3087, 8352, 6174])
>>> kaprekar_constant(6174)
(0, [6174])
>>> kaprekar_constant(1234)
(3, [1234, 3087, 8352, 6174])
>>> kaprekar_constant(1111)
(-1, [1111, 0])
>>> kaprekar_constant(495)
(4, [495, 9081, 9621, 8352, 6174])
>>> kaprekar_constant(9998)
(5, [9998, 999, 8991, 8082, 8532, 6174])
"""
if not isinstance(number, int) or number < 0 or number > 9999:
msg = f"Input must be a non-negative integer between 0 and 9999, got {number}"
raise ValueError(msg)
if not isinstance(max_iterations, int) or max_iterations < 1:
msg = f"max_iterations must be a positive integer, got {max_iterations}"
raise ValueError(msg)
sequence = [number]
current = number
# If already at Kaprekar's constant
if current == 6174:
return (0, sequence)
for iteration in range(max_iterations):
current = kaprekar_routine(current)
sequence.append(current)
# Check if we've reached the constant
if current == 6174:
return (iteration + 1, sequence)
# Check if we've reached a repdigit (all same digits) which gives 0
if current == 0:
return (-1, sequence)
# Did not reach 6174 within max_iterations
return (-1, sequence)
def is_kaprekar_valid(number: int) -> bool:
"""
Check if a number is valid for Kaprekar's routine.
A number is valid if it has at least two different digits.
Args:
number: A 4-digit number (0-9999)
Returns:
True if the number is valid for Kaprekar's routine, False otherwise
>>> is_kaprekar_valid(3524)
True
>>> is_kaprekar_valid(1111)
False
>>> is_kaprekar_valid(1000)
True
>>> is_kaprekar_valid(1)
True
>>> is_kaprekar_valid(6174)
True
"""
if not isinstance(number, int) or number < 0 or number > 9999:
msg = f"Input must be a non-negative integer between 0 and 9999, got {number}"
raise ValueError(msg)
# Convert to 4-digit string with leading zeros
digits = str(number).zfill(4)
# Check if at least two different digits exist
return len(set(digits)) > 1
def main() -> None:
"""
Demonstrate Kaprekar's constant with user input and examples.
"""
print("Kaprekar's Constant (6174) - Demonstration\n")
print("=" * 50)
# Example demonstrations
examples = [3524, 1234, 6174, 9998, 1111, 5]
for num in examples:
print(f"\nStarting number: {num:04d}")
if not is_kaprekar_valid(num):
print(" ❌ Invalid: All digits are the same (repdigit)")
continue
iterations, sequence = kaprekar_constant(num)
if iterations == -1:
print(f" ❌ Did not reach 6174. Sequence: {sequence}")
elif iterations == 0:
print(" ✓ Already at Kaprekar's constant!")
else:
print(f" ✓ Reached 6174 in {iterations} iteration(s)")
print(f" Sequence: {' -> '.join(str(n) for n in sequence)}")
# Interactive mode
print("\n" + "=" * 50)
print("\nTry your own number (0-9999):")
try:
user_input = int(input("Enter a 4-digit number: "))
if 0 <= user_input <= 9999:
if not is_kaprekar_valid(user_input):
print("Invalid: All digits are the same!")
else:
iterations, sequence = kaprekar_constant(user_input)
print(f"\nSequence: {' -> '.join(str(n) for n in sequence)}")
if iterations != -1:
print(f"Reached 6174 in {iterations} iteration(s)!")
else:
print("Number must be between 0 and 9999")
except ValueError, EOFError:
print("Invalid input or no input provided")
if __name__ == "__main__":
import doctest
doctest.testmod()
main()