From 9e4f9962a02ae584b392670a13d54ef8731e8f7f Mon Sep 17 00:00:00 2001 From: David Ekong <66387173+davidekong@users.noreply.github.com> Date: Wed, 6 Sep 2023 15:00:09 +0100 Subject: [PATCH] Created harshad_numbers.py (#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 * Update maths/harshad_numbers.py Co-authored-by: Tianyi Zheng * Raised Value Error for negative inputs * Update maths/harshad_numbers.py Co-authored-by: Tianyi Zheng * Update maths/harshad_numbers.py Co-authored-by: Tianyi Zheng * Update maths/harshad_numbers.py Co-authored-by: Tianyi Zheng * 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 --- maths/harshad_numbers.py | 158 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 maths/harshad_numbers.py diff --git a/maths/harshad_numbers.py b/maths/harshad_numbers.py new file mode 100644 index 000000000..050c69e0b --- /dev/null +++ b/maths/harshad_numbers.py @@ -0,0 +1,158 @@ +""" +A harshad number (or more specifically an n-harshad number) is a number that's +divisible by the sum of its digits in some given base n. +Reference: https://en.wikipedia.org/wiki/Harshad_number +""" + + +def int_to_base(number: int, base: int) -> str: + """ + Convert a given positive decimal integer to base 'base'. + Where 'base' ranges from 2 to 36. + + Examples: + >>> int_to_base(23, 2) + '10111' + >>> int_to_base(58, 5) + '213' + >>> int_to_base(167, 16) + 'A7' + >>> # bases below 2 and beyond 36 will error + >>> int_to_base(98, 1) + Traceback (most recent call last): + ... + ValueError: 'base' must be between 2 and 36 inclusive + >>> int_to_base(98, 37) + Traceback (most recent call last): + ... + ValueError: 'base' must be between 2 and 36 inclusive + """ + + if base < 2 or base > 36: + raise ValueError("'base' must be between 2 and 36 inclusive") + + digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" + result = "" + + if number < 0: + raise ValueError("number must be a positive integer") + + while number > 0: + number, remainder = divmod(number, base) + result = digits[remainder] + result + + if result == "": + result = "0" + + return result + + +def sum_of_digits(num: int, base: int) -> str: + """ + Calculate the sum of digit values in a positive integer + converted to the given 'base'. + Where 'base' ranges from 2 to 36. + + Examples: + >>> sum_of_digits(103, 12) + '13' + >>> sum_of_digits(1275, 4) + '30' + >>> sum_of_digits(6645, 2) + '1001' + >>> # bases below 2 and beyond 36 will error + >>> sum_of_digits(543, 1) + Traceback (most recent call last): + ... + ValueError: 'base' must be between 2 and 36 inclusive + >>> sum_of_digits(543, 37) + Traceback (most recent call last): + ... + ValueError: 'base' must be between 2 and 36 inclusive + """ + + if base < 2 or base > 36: + raise ValueError("'base' must be between 2 and 36 inclusive") + + num_str = int_to_base(num, base) + res = sum(int(char, base) for char in num_str) + res_str = int_to_base(res, base) + return res_str + + +def harshad_numbers_in_base(limit: int, base: int) -> list[str]: + """ + Finds all Harshad numbers smaller than num in base 'base'. + Where 'base' ranges from 2 to 36. + + Examples: + >>> harshad_numbers_in_base(15, 2) + ['1', '10', '100', '110', '1000', '1010', '1100'] + >>> harshad_numbers_in_base(12, 34) + ['1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B'] + >>> harshad_numbers_in_base(12, 4) + ['1', '2', '3', '10', '12', '20', '21'] + >>> # bases below 2 and beyond 36 will error + >>> harshad_numbers_in_base(234, 37) + Traceback (most recent call last): + ... + ValueError: 'base' must be between 2 and 36 inclusive + >>> harshad_numbers_in_base(234, 1) + Traceback (most recent call last): + ... + ValueError: 'base' must be between 2 and 36 inclusive + """ + + if base < 2 or base > 36: + raise ValueError("'base' must be between 2 and 36 inclusive") + + if limit < 0: + return [] + + numbers = [ + int_to_base(i, base) + for i in range(1, limit) + if i % int(sum_of_digits(i, base), base) == 0 + ] + + return numbers + + +def is_harshad_number_in_base(num: int, base: int) -> bool: + """ + Determines whether n in base 'base' is a harshad number. + Where 'base' ranges from 2 to 36. + + Examples: + >>> is_harshad_number_in_base(18, 10) + True + >>> is_harshad_number_in_base(21, 10) + True + >>> is_harshad_number_in_base(-21, 5) + False + >>> # bases below 2 and beyond 36 will error + >>> is_harshad_number_in_base(45, 37) + Traceback (most recent call last): + ... + ValueError: 'base' must be between 2 and 36 inclusive + >>> is_harshad_number_in_base(45, 1) + Traceback (most recent call last): + ... + ValueError: 'base' must be between 2 and 36 inclusive + """ + + if base < 2 or base > 36: + raise ValueError("'base' must be between 2 and 36 inclusive") + + if num < 0: + return False + + n = int_to_base(num, base) + d = sum_of_digits(num, base) + return int(n, base) % int(d, base) == 0 + + +if __name__ == "__main__": + import doctest + + doctest.testmod()