mirror of
https://github.com/TheAlgorithms/Python.git
synced 2026-09-28 21:45:27 +08:00
Add improved doctrings and doctests for math/perfect_number.py (#12830)
* Add improved doctrings and doctests for math/perfect_number.py * [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: Christian Clauss <cclauss@me.com>
This commit is contained in:
co-authored by
pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Christian Clauss
parent
301d111954
commit
beca49787a
+270
-52
@@ -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("Running doctests...")
|
||||
testmod(verbose=True)
|
||||
|
||||
print(f"{number} is {'' if perfect(number) else 'not '}a Perfect Number.")
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user