Skip to content

Commit 9e4f996

Browse files
davidekongpre-commit-ci[bot]tianyizheng02
authored
Created harshad_numbers.py (TheAlgorithms#9023)
* Created harshad_numbers.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update harshad_numbers.py Fixed a few errors * Update harshad_numbers.py Added function type hints * Update harshad_numbers.py Fixed depreciated Tuple and List usage * Update harshad_numbers.py Fixed incompatible types in assignments * Update harshad_numbers.py Fixed incompatible type assignments * Update maths/harshad_numbers.py Co-authored-by: Tianyi Zheng <[email protected]> * Update maths/harshad_numbers.py Co-authored-by: Tianyi Zheng <[email protected]> * Raised Value Error for negative inputs * Update maths/harshad_numbers.py Co-authored-by: Tianyi Zheng <[email protected]> * Update maths/harshad_numbers.py Co-authored-by: Tianyi Zheng <[email protected]> * Update maths/harshad_numbers.py Co-authored-by: Tianyi Zheng <[email protected]> * Update harshad_numbers.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update harshad_numbers.py * Update harshad_numbers.py * Update harshad_numbers.py * Update harshad_numbers.py Added doc test to int_to_base, fixed nested loop, other minor changes * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Tianyi Zheng <[email protected]>
1 parent 72f6000 commit 9e4f996

File tree

1 file changed

+158
-0
lines changed

1 file changed

+158
-0
lines changed

maths/harshad_numbers.py

+158
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
"""
2+
A harshad number (or more specifically an n-harshad number) is a number that's
3+
divisible by the sum of its digits in some given base n.
4+
Reference: https://en.wikipedia.org/wiki/Harshad_number
5+
"""
6+
7+
8+
def int_to_base(number: int, base: int) -> str:
9+
"""
10+
Convert a given positive decimal integer to base 'base'.
11+
Where 'base' ranges from 2 to 36.
12+
13+
Examples:
14+
>>> int_to_base(23, 2)
15+
'10111'
16+
>>> int_to_base(58, 5)
17+
'213'
18+
>>> int_to_base(167, 16)
19+
'A7'
20+
>>> # bases below 2 and beyond 36 will error
21+
>>> int_to_base(98, 1)
22+
Traceback (most recent call last):
23+
...
24+
ValueError: 'base' must be between 2 and 36 inclusive
25+
>>> int_to_base(98, 37)
26+
Traceback (most recent call last):
27+
...
28+
ValueError: 'base' must be between 2 and 36 inclusive
29+
"""
30+
31+
if base < 2 or base > 36:
32+
raise ValueError("'base' must be between 2 and 36 inclusive")
33+
34+
digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
35+
result = ""
36+
37+
if number < 0:
38+
raise ValueError("number must be a positive integer")
39+
40+
while number > 0:
41+
number, remainder = divmod(number, base)
42+
result = digits[remainder] + result
43+
44+
if result == "":
45+
result = "0"
46+
47+
return result
48+
49+
50+
def sum_of_digits(num: int, base: int) -> str:
51+
"""
52+
Calculate the sum of digit values in a positive integer
53+
converted to the given 'base'.
54+
Where 'base' ranges from 2 to 36.
55+
56+
Examples:
57+
>>> sum_of_digits(103, 12)
58+
'13'
59+
>>> sum_of_digits(1275, 4)
60+
'30'
61+
>>> sum_of_digits(6645, 2)
62+
'1001'
63+
>>> # bases below 2 and beyond 36 will error
64+
>>> sum_of_digits(543, 1)
65+
Traceback (most recent call last):
66+
...
67+
ValueError: 'base' must be between 2 and 36 inclusive
68+
>>> sum_of_digits(543, 37)
69+
Traceback (most recent call last):
70+
...
71+
ValueError: 'base' must be between 2 and 36 inclusive
72+
"""
73+
74+
if base < 2 or base > 36:
75+
raise ValueError("'base' must be between 2 and 36 inclusive")
76+
77+
num_str = int_to_base(num, base)
78+
res = sum(int(char, base) for char in num_str)
79+
res_str = int_to_base(res, base)
80+
return res_str
81+
82+
83+
def harshad_numbers_in_base(limit: int, base: int) -> list[str]:
84+
"""
85+
Finds all Harshad numbers smaller than num in base 'base'.
86+
Where 'base' ranges from 2 to 36.
87+
88+
Examples:
89+
>>> harshad_numbers_in_base(15, 2)
90+
['1', '10', '100', '110', '1000', '1010', '1100']
91+
>>> harshad_numbers_in_base(12, 34)
92+
['1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B']
93+
>>> harshad_numbers_in_base(12, 4)
94+
['1', '2', '3', '10', '12', '20', '21']
95+
>>> # bases below 2 and beyond 36 will error
96+
>>> harshad_numbers_in_base(234, 37)
97+
Traceback (most recent call last):
98+
...
99+
ValueError: 'base' must be between 2 and 36 inclusive
100+
>>> harshad_numbers_in_base(234, 1)
101+
Traceback (most recent call last):
102+
...
103+
ValueError: 'base' must be between 2 and 36 inclusive
104+
"""
105+
106+
if base < 2 or base > 36:
107+
raise ValueError("'base' must be between 2 and 36 inclusive")
108+
109+
if limit < 0:
110+
return []
111+
112+
numbers = [
113+
int_to_base(i, base)
114+
for i in range(1, limit)
115+
if i % int(sum_of_digits(i, base), base) == 0
116+
]
117+
118+
return numbers
119+
120+
121+
def is_harshad_number_in_base(num: int, base: int) -> bool:
122+
"""
123+
Determines whether n in base 'base' is a harshad number.
124+
Where 'base' ranges from 2 to 36.
125+
126+
Examples:
127+
>>> is_harshad_number_in_base(18, 10)
128+
True
129+
>>> is_harshad_number_in_base(21, 10)
130+
True
131+
>>> is_harshad_number_in_base(-21, 5)
132+
False
133+
>>> # bases below 2 and beyond 36 will error
134+
>>> is_harshad_number_in_base(45, 37)
135+
Traceback (most recent call last):
136+
...
137+
ValueError: 'base' must be between 2 and 36 inclusive
138+
>>> is_harshad_number_in_base(45, 1)
139+
Traceback (most recent call last):
140+
...
141+
ValueError: 'base' must be between 2 and 36 inclusive
142+
"""
143+
144+
if base < 2 or base > 36:
145+
raise ValueError("'base' must be between 2 and 36 inclusive")
146+
147+
if num < 0:
148+
return False
149+
150+
n = int_to_base(num, base)
151+
d = sum_of_digits(num, base)
152+
return int(n, base) % int(d, base) == 0
153+
154+
155+
if __name__ == "__main__":
156+
import doctest
157+
158+
doctest.testmod()

0 commit comments

Comments
 (0)