Skip to content

Commit 27e48ba

Browse files
author
Kcstring
committed
maths/special_numbers: add Kaprekar number implementation
Add is_kaprekar_number() and kaprekar_numbers() with doctests. Implements OEIS A006886 — numbers whose square splits into two parts that sum back to the original number.
1 parent 791deb4 commit 27e48ba

1 file changed

Lines changed: 104 additions & 0 deletions

File tree

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
"""
2+
A Kaprekar number is a non-negative integer whose square can be split into two parts
3+
that sum back to the original number.
4+
5+
For example:
6+
- 9: 9² = 81, and 8 + 1 = 9
7+
- 45: 45² = 2025, and 20 + 25 = 45
8+
- 297: 297² = 88209, and 88 + 209 = 297
9+
10+
Note: The right part may have leading zeros (e.g., 1: 1² = 1, split as 0 + 1 = 1).
11+
12+
On-Line Encyclopedia of Integer Sequences entry: https://oeis.org/A006886
13+
14+
References:
15+
- https://en.wikipedia.org/wiki/Kaprekar_number
16+
"""
17+
18+
19+
def is_kaprekar_number(number: int) -> bool:
20+
"""
21+
Return True if the given number is a Kaprekar number, False otherwise.
22+
23+
A Kaprekar number n has the property that n² can be split into two parts
24+
(right part must be non-empty) whose sum equals n.
25+
26+
>>> is_kaprekar_number(1)
27+
True
28+
>>> is_kaprekar_number(9)
29+
True
30+
>>> is_kaprekar_number(45)
31+
True
32+
>>> is_kaprekar_number(297)
33+
True
34+
>>> is_kaprekar_number(2223)
35+
True
36+
>>> is_kaprekar_number(2)
37+
False
38+
>>> is_kaprekar_number(10)
39+
False
40+
>>> is_kaprekar_number(0)
41+
Traceback (most recent call last):
42+
...
43+
ValueError: number=0 must be a positive integer
44+
>>> is_kaprekar_number(-1)
45+
Traceback (most recent call last):
46+
...
47+
ValueError: number=-1 must be a positive integer
48+
>>> is_kaprekar_number(1.5)
49+
Traceback (most recent call last):
50+
...
51+
ValueError: number=1.5 must be a positive integer
52+
"""
53+
if not isinstance(number, int) or number <= 0:
54+
msg = f"{number=} must be a positive integer"
55+
raise ValueError(msg)
56+
57+
square = number * number
58+
square_str = str(square)
59+
n_digits = len(square_str)
60+
61+
# Try every split position; left part may be empty (treated as 0),
62+
# but right part must be positive (non-zero) to avoid trivial splits.
63+
for split in range(n_digits):
64+
left = int(square_str[:split]) if split > 0 else 0
65+
right = int(square_str[split:])
66+
if right > 0 and left + right == number:
67+
return True
68+
69+
return False
70+
71+
72+
def kaprekar_numbers(limit: int) -> list[int]:
73+
"""
74+
Return a list of all Kaprekar numbers up to and including the given limit.
75+
76+
>>> kaprekar_numbers(1)
77+
[1]
78+
>>> kaprekar_numbers(100)
79+
[1, 9, 45, 55, 99]
80+
>>> kaprekar_numbers(1000)
81+
[1, 9, 45, 55, 99, 297, 703, 999]
82+
>>> kaprekar_numbers(0)
83+
Traceback (most recent call last):
84+
...
85+
ValueError: limit=0 must be a positive integer
86+
>>> kaprekar_numbers(-5)
87+
Traceback (most recent call last):
88+
...
89+
ValueError: limit=-5 must be a positive integer
90+
"""
91+
if not isinstance(limit, int) or limit <= 0:
92+
msg = f"{limit=} must be a positive integer"
93+
raise ValueError(msg)
94+
95+
return [n for n in range(1, limit + 1) if is_kaprekar_number(n)]
96+
97+
98+
if __name__ == "__main__":
99+
import doctest
100+
101+
doctest.testmod()
102+
103+
print("Kaprekar numbers up to 10000:")
104+
print(kaprekar_numbers(10000))

0 commit comments

Comments
 (0)