-
-
Notifications
You must be signed in to change notification settings - Fork 51k
Expand file tree
/
Copy pathperfect_number.py
More file actions
304 lines (246 loc) · 7.79 KB
/
Copy pathperfect_number.py
File metadata and controls
304 lines (246 loc) · 7.79 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
"""
== 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
The first few perfect numbers are: 6, 28, 496, 8128, 33550336, ...
https://en.wikipedia.org/wiki/Perfect_number
https://oeis.org/A000396
"""
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 (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 positive integer to be checked.
Returns:
True if the number is a perfect number, False otherwise.
Raises:
ValueError: If number is not an integer.
Examples:
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
# 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
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