From 5d812c953f100e4eb341f632d10fa75cd6419c6b Mon Sep 17 00:00:00 2001 From: Achintya Sharma Date: Sun, 6 Jul 2025 20:41:47 +0530 Subject: [PATCH 1/2] Add improved doctrings and doctests for math/perfect_number.py --- maths/perfect_number.py | 324 +++++++++++++++++++++++++++++++++------- 1 file changed, 271 insertions(+), 53 deletions(-) diff --git a/maths/perfect_number.py b/maths/perfect_number.py index 52c816cc7895..dc4d55c27fc5 100644 --- a/maths/perfect_number.py +++ b/maths/perfect_number.py @@ -2,13 +2,15 @@ == Perfect Number == In number theory, a perfect number is a positive integer that is equal to the sum of its positive divisors, excluding the number itself. + For example: 6 ==> divisors[1, 2, 3, 6] Excluding 6, the sum(divisors) is 1 + 2 + 3 = 6 So, 6 is a Perfect Number -Other examples of Perfect Numbers: 28, 486, ... +The first few perfect numbers are: 6, 28, 496, 8128, 33550336, ... https://en.wikipedia.org/wiki/Perfect_number +https://oeis.org/A000396 """ @@ -17,70 +19,286 @@ def perfect(number: int) -> bool: Check if a number is a perfect number. A perfect number is a positive integer that is equal to the sum of its proper - divisors (excluding itself). + divisors (positive divisors excluding the number itself). + + The algorithm finds all divisors up to number//2 (since no proper divisor + can be greater than half the number) and sums them for comparison. + + Time Complexity: O(sqrt(n)) with optimized divisor finding + Space Complexity: O(1) Args: - number: The number to be checked. + number: The positive integer to be checked. Returns: - True if the number is a perfect number otherwise, False. - Start from 1 because dividing by 0 will raise ZeroDivisionError. - A number at most can be divisible by the half of the number except the number - itself. For example, 6 is at most can be divisible by 3 except by 6 itself. + True if the number is a perfect number, False otherwise. + + Raises: + ValueError: If number is not an integer. + Examples: - >>> perfect(27) - False - >>> perfect(28) - True - >>> perfect(29) - False - >>> perfect(6) - True - >>> perfect(12) - False - >>> perfect(496) - True - >>> perfect(8128) - True - >>> perfect(0) - False - >>> perfect(-1) - False - >>> perfect(33550336) # Large perfect number - True - >>> perfect(33550337) # Just above a large perfect number - False - >>> perfect(1) # Edge case: 1 is not a perfect number - False - >>> perfect("123") # String representation of a number - Traceback (most recent call last): - ... - ValueError: number must be an integer - >>> perfect(12.34) - Traceback (most recent call last): - ... - ValueError: number must be an integer - >>> perfect("Hello") - Traceback (most recent call last): - ... - ValueError: number must be an integer + Basic perfect numbers: + >>> perfect(6) + True + >>> perfect(28) + True + >>> perfect(496) + True + >>> perfect(8128) + True + + Large perfect number: + >>> perfect(33550336) + True + + Non-perfect numbers: + >>> perfect(12) + False + >>> perfect(27) + False + >>> perfect(29) + False + >>> perfect(100) + False + + Edge cases: + >>> perfect(1) + False + >>> perfect(2) + False + >>> perfect(0) + False + >>> perfect(-1) + False + >>> perfect(-6) + False + + Numbers close to perfect numbers: + >>> perfect(5) + False + >>> perfect(7) + False + >>> perfect(27) + False + >>> perfect(29) + False + >>> perfect(495) + False + >>> perfect(497) + False + >>> perfect(33550335) + False + >>> perfect(33550337) + False + + Type validation: + >>> perfect(12.34) + Traceback (most recent call last): + ... + ValueError: number must be an integer + >>> perfect("123") + Traceback (most recent call last): + ... + ValueError: number must be an integer + >>> perfect("Hello") + Traceback (most recent call last): + ... + ValueError: number must be an integer + >>> perfect([6]) + Traceback (most recent call last): + ... + ValueError: number must be an integer + >>> perfect(None) + Traceback (most recent call last): + ... + ValueError: number must be an integer + + Testing divisor sum calculation for known cases: + >>> # For 6: divisors are 1, 2, 3 -> sum = 6 + >>> sum(i for i in range(1, 6//2 + 1) if 6 % i == 0) == 6 + True + >>> # For 28: divisors are 1, 2, 4, 7, 14 -> sum = 28 + >>> sum(i for i in range(1, 28//2 + 1) if 28 % i == 0) == 28 + True + >>> # For 12: divisors are 1, 2, 3, 4, 6 -> sum = 16 ≠ 12 + >>> sum(i for i in range(1, 12//2 + 1) if 12 % i == 0) == 12 + False """ if not isinstance(number, int): raise ValueError("number must be an integer") + if number <= 0: return False - return sum(i for i in range(1, number // 2 + 1) if number % i == 0) == number + + # Special case: 1 has no proper divisors + if number == 1: + return False + + # Find sum of all proper divisors + # We only need to check up to number//2 since no proper divisor + # can be greater than half the number + divisor_sum = sum(i for i in range(1, number // 2 + 1) if number % i == 0) + + return divisor_sum == number + + +def perfect_optimized(number: int) -> bool: + """ + Optimized version of perfect number checker using mathematical properties. + + This version uses the fact that divisors come in pairs (d, n/d) to reduce + the search space to sqrt(n). + + Time Complexity: O(sqrt(n)) + Space Complexity: O(1) + + Args: + number: The positive integer to be checked. + + Returns: + True if the number is a perfect number, False otherwise. + + Examples: + >>> perfect_optimized(6) + True + >>> perfect_optimized(28) + True + >>> perfect_optimized(496) + True + >>> perfect_optimized(12) + False + >>> perfect_optimized(1) + False + >>> perfect_optimized(0) + False + >>> perfect_optimized(-1) + False + """ + if not isinstance(number, int): + raise ValueError("number must be an integer") + + if number <= 1: + return False + + divisor_sum = 1 # 1 is always a proper divisor for n > 1 + + # Check divisors up to sqrt(number) + i = 2 + while i * i <= number: + if number % i == 0: + divisor_sum += i + # Add the paired divisor if it's different from i + if i != number // i: + divisor_sum += number // i + i += 1 + + return divisor_sum == number + + +def find_perfect_numbers(limit: int) -> list[int]: + """ + Find all perfect numbers up to a given limit. + + Args: + limit: The upper bound to search for perfect numbers. + + Returns: + List of perfect numbers up to the limit. + + Examples: + >>> find_perfect_numbers(10) + [6] + >>> find_perfect_numbers(30) + [6, 28] + >>> find_perfect_numbers(500) + [6, 28, 496] + >>> find_perfect_numbers(0) + [] + >>> find_perfect_numbers(1) + [] + """ + if not isinstance(limit, int) or limit < 0: + raise ValueError("limit must be a non-negative integer") + + return [n for n in range(1, limit + 1) if perfect(n)] + + +def get_divisors(number: int) -> list[int]: + """ + Get all proper divisors of a number (excluding the number itself). + + Args: + number: The positive integer to find divisors for. + + Returns: + List of proper divisors in ascending order. + + Examples: + >>> get_divisors(6) + [1, 2, 3] + >>> get_divisors(28) + [1, 2, 4, 7, 14] + >>> get_divisors(12) + [1, 2, 3, 4, 6] + >>> get_divisors(1) + [] + >>> get_divisors(7) + [1] + """ + if not isinstance(number, int) or number <= 0: + raise ValueError("number must be a positive integer") + + if number == 1: + return [] + + return [i for i in range(1, number // 2 + 1) if number % i == 0] if __name__ == "__main__": from doctest import testmod - testmod() - print("Program to check whether a number is a Perfect number or not...") - try: - number = int(input("Enter a positive integer: ").strip()) - except ValueError: - msg = "number must be an integer" - raise ValueError(msg) - - print(f"{number} is {'' if perfect(number) else 'not '}a Perfect Number.") + print("Running doctests...") + testmod(verbose=True) + + print("\nPerfect Number Checker") + print("=" * 40) + print("A perfect number equals the sum of its proper divisors.") + print("Examples: 6 (1+2+3), 28 (1+2+4+7+14), 496, 8128, ...") + print() + + while True: + try: + user_input = input("Enter a positive integer (or 'q' to quit): ").strip() + if user_input.lower() == 'q': + break + + number = int(user_input) + + if number <= 0: + print("Please enter a positive integer.") + continue + + is_perfect = perfect(number) + divisors = get_divisors(number) + divisor_sum = sum(divisors) + + print(f"\nNumber: {number}") + print(f"Proper divisors: {divisors}") + print(f"Sum of divisors: {divisor_sum}") + print(f"Is perfect: {'Yes' if is_perfect else 'No'}") + + if is_perfect: + print(f"✓ {number} is a Perfect Number!") + else: + print(f"✗ {number} is not a Perfect Number.") + + print("-" * 40) + + except ValueError as e: + if "invalid literal" in str(e): + print("Please enter a valid integer.") + else: + print(f"Error: {e}") + except KeyboardInterrupt: + print("\nGoodbye!") + break \ No newline at end of file From b6ea72123db1af3df6d882cc81c20d2ba1b94e34 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 6 Jul 2025 15:16:14 +0000 Subject: [PATCH 2/2] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- maths/perfect_number.py | 66 ++++++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/maths/perfect_number.py b/maths/perfect_number.py index dc4d55c27fc5..2aaaebbd33d9 100644 --- a/maths/perfect_number.py +++ b/maths/perfect_number.py @@ -126,38 +126,38 @@ def perfect(number: int) -> bool: """ if not isinstance(number, int): raise ValueError("number must be an integer") - + if number <= 0: return False - + # Special case: 1 has no proper divisors if number == 1: return False - + # Find sum of all proper divisors # We only need to check up to number//2 since no proper divisor # can be greater than half the number divisor_sum = sum(i for i in range(1, number // 2 + 1) if number % i == 0) - + return divisor_sum == number def perfect_optimized(number: int) -> bool: """ Optimized version of perfect number checker using mathematical properties. - + This version uses the fact that divisors come in pairs (d, n/d) to reduce the search space to sqrt(n). - + Time Complexity: O(sqrt(n)) Space Complexity: O(1) - + Args: number: The positive integer to be checked. - + Returns: True if the number is a perfect number, False otherwise. - + Examples: >>> perfect_optimized(6) True @@ -176,12 +176,12 @@ def perfect_optimized(number: int) -> bool: """ if not isinstance(number, int): raise ValueError("number must be an integer") - + if number <= 1: return False - + divisor_sum = 1 # 1 is always a proper divisor for n > 1 - + # Check divisors up to sqrt(number) i = 2 while i * i <= number: @@ -191,20 +191,20 @@ def perfect_optimized(number: int) -> bool: if i != number // i: divisor_sum += number // i i += 1 - + return divisor_sum == number def find_perfect_numbers(limit: int) -> list[int]: """ Find all perfect numbers up to a given limit. - + Args: limit: The upper bound to search for perfect numbers. - + Returns: List of perfect numbers up to the limit. - + Examples: >>> find_perfect_numbers(10) [6] @@ -219,20 +219,20 @@ def find_perfect_numbers(limit: int) -> list[int]: """ if not isinstance(limit, int) or limit < 0: raise ValueError("limit must be a non-negative integer") - + return [n for n in range(1, limit + 1) if perfect(n)] def get_divisors(number: int) -> list[int]: """ Get all proper divisors of a number (excluding the number itself). - + Args: number: The positive integer to find divisors for. - + Returns: List of proper divisors in ascending order. - + Examples: >>> get_divisors(6) [1, 2, 3] @@ -247,10 +247,10 @@ def get_divisors(number: int) -> list[int]: """ if not isinstance(number, int) or number <= 0: raise ValueError("number must be a positive integer") - + if number == 1: return [] - + return [i for i in range(1, number // 2 + 1) if number % i == 0] @@ -259,41 +259,41 @@ def get_divisors(number: int) -> list[int]: print("Running doctests...") testmod(verbose=True) - + print("\nPerfect Number Checker") print("=" * 40) print("A perfect number equals the sum of its proper divisors.") print("Examples: 6 (1+2+3), 28 (1+2+4+7+14), 496, 8128, ...") print() - + while True: try: user_input = input("Enter a positive integer (or 'q' to quit): ").strip() - if user_input.lower() == 'q': + if user_input.lower() == "q": break - + number = int(user_input) - + if number <= 0: print("Please enter a positive integer.") continue - + is_perfect = perfect(number) divisors = get_divisors(number) divisor_sum = sum(divisors) - + print(f"\nNumber: {number}") print(f"Proper divisors: {divisors}") print(f"Sum of divisors: {divisor_sum}") print(f"Is perfect: {'Yes' if is_perfect else 'No'}") - + if is_perfect: print(f"✓ {number} is a Perfect Number!") else: print(f"✗ {number} is not a Perfect Number.") - + print("-" * 40) - + except ValueError as e: if "invalid literal" in str(e): print("Please enter a valid integer.") @@ -301,4 +301,4 @@ def get_divisors(number: int) -> list[int]: print(f"Error: {e}") except KeyboardInterrupt: print("\nGoodbye!") - break \ No newline at end of file + break