Skip to content

Commit 24f9a23

Browse files
committed
feat: add Kaprekar's Constant (6174) algorithm to special numbers
1 parent af17867 commit 24f9a23

File tree

1 file changed

+206
-0
lines changed

1 file changed

+206
-0
lines changed
Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
"""
2+
Kaprekar's Constant (6174) - also known as Kaprekar's Routine
3+
4+
Kaprekar's constant is a special number discovered by Indian mathematician
5+
D. R. Kaprekar in 1949. It is notable for the following property:
6+
7+
1. Take any four-digit number with at least two different digits (leading zeros allowed)
8+
2. Arrange the digits in descending order to form the largest possible number
9+
3. Arrange the digits in ascending order to form the smallest possible number
10+
4. Subtract the smaller number from the larger number
11+
5. Repeat the process with the result
12+
13+
This process will always reach 6174 in at most 7 iterations, and once 6174 is
14+
reached, the process will continue to yield 6174.
15+
16+
Example:
17+
3524 -> 5432 - 2345 = 3087
18+
3087 -> 8730 - 0378 = 8352
19+
8352 -> 8532 - 2358 = 6174
20+
6174 -> 7641 - 1467 = 6174 (repeats)
21+
22+
Reference: https://en.wikipedia.org/wiki/6174_(number)
23+
OEIS: https://oeis.org/A099009
24+
"""
25+
26+
27+
def kaprekar_routine(number: int) -> int:
28+
"""
29+
Perform one iteration of Kaprekar's routine.
30+
31+
Args:
32+
number: A 4-digit number (0-9999)
33+
34+
Returns:
35+
The result of one Kaprekar iteration
36+
37+
>>> kaprekar_routine(3524)
38+
3087
39+
>>> kaprekar_routine(3087)
40+
8352
41+
>>> kaprekar_routine(8352)
42+
6174
43+
>>> kaprekar_routine(6174)
44+
6174
45+
>>> kaprekar_routine(1)
46+
999
47+
>>> kaprekar_routine(1111)
48+
0
49+
"""
50+
if not isinstance(number, int) or number < 0 or number > 9999:
51+
msg = f"Input must be a non-negative integer between 0 and 9999, got {number}"
52+
raise ValueError(msg)
53+
54+
# Convert to 4-digit string with leading zeros
55+
digits = str(number).zfill(4)
56+
57+
# Check if all digits are the same (repdigit)
58+
if len(set(digits)) == 1:
59+
return 0
60+
61+
# Sort digits in descending and ascending order
62+
descending = int("".join(sorted(digits, reverse=True)))
63+
ascending = int("".join(sorted(digits)))
64+
65+
return descending - ascending
66+
67+
68+
def kaprekar_constant(number: int, max_iterations: int = 7) -> tuple[int, list[int]]:
69+
"""
70+
Apply Kaprekar's routine until reaching the constant 6174 or max iterations.
71+
72+
Args:
73+
number: A 4-digit number (0-9999)
74+
max_iterations: Maximum number of iterations to perform (default: 7)
75+
76+
Returns:
77+
A tuple containing:
78+
- The number of iterations taken to reach 6174 (or -1 if not reached)
79+
- A list of all intermediate results
80+
81+
>>> kaprekar_constant(3524)
82+
(3, [3524, 3087, 8352, 6174])
83+
>>> kaprekar_constant(6174)
84+
(0, [6174])
85+
>>> kaprekar_constant(1234)
86+
(3, [1234, 3087, 8352, 6174])
87+
>>> kaprekar_constant(1111)
88+
(-1, [1111, 0])
89+
>>> kaprekar_constant(495)
90+
(4, [495, 9081, 9621, 8352, 6174])
91+
>>> kaprekar_constant(9998)
92+
(5, [9998, 999, 8991, 8082, 8532, 6174])
93+
"""
94+
if not isinstance(number, int) or number < 0 or number > 9999:
95+
msg = f"Input must be a non-negative integer between 0 and 9999, got {number}"
96+
raise ValueError(msg)
97+
98+
if not isinstance(max_iterations, int) or max_iterations < 1:
99+
msg = f"max_iterations must be a positive integer, got {max_iterations}"
100+
raise ValueError(msg)
101+
102+
sequence = [number]
103+
current = number
104+
105+
# If already at Kaprekar's constant
106+
if current == 6174:
107+
return (0, sequence)
108+
109+
for iteration in range(max_iterations):
110+
current = kaprekar_routine(current)
111+
sequence.append(current)
112+
113+
# Check if we've reached the constant
114+
if current == 6174:
115+
return (iteration + 1, sequence)
116+
117+
# Check if we've reached a repdigit (all same digits) which gives 0
118+
if current == 0:
119+
return (-1, sequence)
120+
121+
# Did not reach 6174 within max_iterations
122+
return (-1, sequence)
123+
124+
125+
def is_kaprekar_valid(number: int) -> bool:
126+
"""
127+
Check if a number is valid for Kaprekar's routine.
128+
A number is valid if it has at least two different digits.
129+
130+
Args:
131+
number: A 4-digit number (0-9999)
132+
133+
Returns:
134+
True if the number is valid for Kaprekar's routine, False otherwise
135+
136+
>>> is_kaprekar_valid(3524)
137+
True
138+
>>> is_kaprekar_valid(1111)
139+
False
140+
>>> is_kaprekar_valid(1000)
141+
True
142+
>>> is_kaprekar_valid(1)
143+
True
144+
>>> is_kaprekar_valid(6174)
145+
True
146+
"""
147+
if not isinstance(number, int) or number < 0 or number > 9999:
148+
msg = f"Input must be a non-negative integer between 0 and 9999, got {number}"
149+
raise ValueError(msg)
150+
151+
# Convert to 4-digit string with leading zeros
152+
digits = str(number).zfill(4)
153+
154+
# Check if at least two different digits exist
155+
return len(set(digits)) > 1
156+
157+
158+
def main() -> None:
159+
"""
160+
Demonstrate Kaprekar's constant with user input and examples.
161+
"""
162+
print("Kaprekar's Constant (6174) - Demonstration\n")
163+
print("=" * 50)
164+
165+
# Example demonstrations
166+
examples = [3524, 1234, 6174, 9998, 1111, 5]
167+
168+
for num in examples:
169+
print(f"\nStarting number: {num:04d}")
170+
if not is_kaprekar_valid(num):
171+
print(" ❌ Invalid: All digits are the same (repdigit)")
172+
continue
173+
174+
iterations, sequence = kaprekar_constant(num)
175+
if iterations == -1:
176+
print(f" ❌ Did not reach 6174. Sequence: {sequence}")
177+
elif iterations == 0:
178+
print(" ✓ Already at Kaprekar's constant!")
179+
else:
180+
print(f" ✓ Reached 6174 in {iterations} iteration(s)")
181+
print(f" Sequence: {' -> '.join(str(n) for n in sequence)}")
182+
183+
# Interactive mode
184+
print("\n" + "=" * 50)
185+
print("\nTry your own number (0-9999):")
186+
try:
187+
user_input = int(input("Enter a 4-digit number: "))
188+
if 0 <= user_input <= 9999:
189+
if not is_kaprekar_valid(user_input):
190+
print("Invalid: All digits are the same!")
191+
else:
192+
iterations, sequence = kaprekar_constant(user_input)
193+
print(f"\nSequence: {' -> '.join(str(n) for n in sequence)}")
194+
if iterations != -1:
195+
print(f"Reached 6174 in {iterations} iteration(s)!")
196+
else:
197+
print("Number must be between 0 and 9999")
198+
except (ValueError, EOFError):
199+
print("Invalid input or no input provided")
200+
201+
202+
if __name__ == "__main__":
203+
import doctest
204+
205+
doctest.testmod()
206+
main()

0 commit comments

Comments
 (0)