507. Perfect Number
Problem Description
The problem requires us to determine whether a given integer n
is a perfect number or not. A perfect number is defined as a positive integer which is equal to the sum of its positive divisors, excluding the number itself. This means we need to find all the integers that can divide n
without leaving a remainder, sum them up, and compare this sum to n
to confirm if n
is a perfect number. Examples of perfect numbers are 6, 28, and 496, where each of them equals the sum of their respective divisors (1, 2, 3 for 6; 1, 2, 4, 7, 14 for 28; etc.).
Intuition
When thinking about how to determine if a number is perfect, the straightforward approach is to iterate through all numbers less than n
to find its divisors and calculate the sum. However, this is not efficient for large values of n
. To optimize this process, we can take advantage of two key insights:
-
Divisors come in pairs. For a divisor
i
, there's often a corresponding divisorn/i
(except wheni
is the square root ofn
, in which case it should not be counted twice). -
We only need to iterate up to the square root of
n
. This is because ifn
has a divisor larger than its square root, the corresponding smaller divisor has already been accounted for in the pair.
The provided solution uses these insights to iterate only up to the square root of n
, adding both i
and n/i
to the sum s
anytime i
is a divisor of n
. Special care is taken for the square root case so it is not added twice. After the loop, if the sum s
equals n
, it is indeed a perfect number, and the function returns true
. Otherwise, it returns false
.
It's also important to note that 1
is not considered a perfect number as per the problem definition and is immediately returned as false
.
Learn more about Math patterns.
Solution Approach
The solution is implemented with a simple function that checks for positive divisors using a while loop. Here are the steps and algorithms used in the provided solution:
-
Special Case Check: Since
1
is not a perfect number (it does not meet the definition's requirement), the function immediately returnsfalse
if the number is1
. -
Initialization: Two variables are initialized -
s
is set to1
(as1
is always a divisor for any number) andi
is set to2
. Here,s
will hold the sum of the divisors, andi
is used to find potential divisors starting from2
. -
Loop Through Potential Divisors: The solution employs a
while
loop that runs as long asi
squared is less than or equal tonum
. This effectively checks all potential divisors up to the square root of the number. -
Check for Divisibility: Inside the loop,
num % i == 0
checks ifi
is a divisor. If it is,i
is added to the sums
. -
Find and Add the Pair Divisor: When a divisor is found, its corresponding pair (
num // i
) is also considered. If the pair divisor is not equal toi
themselves (to avoid adding square roots twice), it is also added to the sums
. -
Increment to Next Potential Divisor: The variable
i
is incremented by1
to check the next potential divisor. -
Final Comparison: After the loop concludes, the sum of the divisors
s
is compared to the original numbernum
. If they are equal, it means that the number is a perfect number, andtrue
is returned. Else,false
is returned.
This approach is efficient since it reduces the number of iterations significantly compared to the brute-force method of checking all numbers less than n
. Furthermore, no additional data structures are used, keeping the space complexity at O(1), and the time complexity is O(√n) since the loop runs up to the square root of num
.
Ready to land your dream job?
Unlock your dream job with a 2-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a small example using the solution approach. Suppose we are given n = 28
and we want to determine if it is a perfect number.
-
Special Case Check: We check if
n
is1
. In this case,n
is28
, so we do not returnfalse
immediately. -
Initialization: We initialize
s
to1
, since1
is a divisor of every number. We also seti
to2
because we'll start checking for divisors from this number. -
Loop Through Potential Divisors: We start the
while
loop, wherei
will go from2
up to the square root of28
. The square root of28
is approximately5.29
, so our loop will run untili
is less than or equal to5
. -
Check for Divisibility and Add Divisors: We check each
i
(from2
to5
) to see if28 % i == 0
. This will be true fori = 2
,i = 4
, andi = 7
. When it's true, we addi
tos
, and ifi
is not the square root of28
, we also add the pair divisor (n / i
):- For
i = 2
,n % i == 28 % 2 == 0
; we add2
tos
. The pair divisor is28 / 2 = 14
, which we also add tos
. Now,s = 1 + 2 + 14 = 17
. - For
i = 3
,n % i == 28 % 3 != 0
; we do not add anything tos
. - For
i = 4
,n % i == 28 % 4 == 0
; we add4
tos
. The pair divisor is28 / 4 = 7
, which we also add tos
. Now,s = 17 + 4 + 7 = 28
. - We do not need to check
i = 5
because28 % 5 != 0
.
- For
-
Increment to Next Potential Divisor: The loop continues, and
i
is incremented tilli > 5
, at which point we stop the loop. -
Final Comparison: The sum
s
is now28
, which is equal to the original numbern
. Thus, we conclude that28
is a perfect number and returntrue
.
Throughout this process, we've checked far fewer divisors than if we had run a loop from 2
to 27
, which illustrates the efficiency of the solution approach. The approach brings down the complexity significantly and works well for checking if a number is a perfect number.
Solution Implementation
1class Solution:
2 def checkPerfectNumber(self, num: int) -> bool:
3 # Perfect numbers are positive integers that are equal to the sum of their proper divisors.
4 # The smallest perfect number is 6, which is the sum of its divisors 1, 2, and 3.
5
6 # A perfect number must be at least 2, as 1 cannot be perfect.
7 if num == 1:
8 return False
9
10 # Initialize the sum of divisors with the first proper divisor, which is always 1.
11 sum_of_divisors = 1
12 # Start checking for other divisors from 2 onwards.
13 divisor = 2
14
15 # Only iterate up to the square root of num to avoid redundant calculations.
16 # Any divisor greater than the square root would have already been accounted for.
17 while divisor * divisor <= num:
18 # If divisor is a proper divisor of num,
19 # then num is divisible by divisor without a remainder.
20 if num % divisor == 0:
21 # Add the divisor to the sum.
22 sum_of_divisors += divisor
23 # If divisor is not the square root of num,
24 # add the corresponding co-divisor (num / divisor).
25 if divisor != num // divisor:
26 sum_of_divisors += num // divisor
27 # Proceed to check the next potential divisor.
28 divisor += 1
29
30 # A number is perfect if the sum of its proper divisors,
31 # including 1 but not the number itself, equals the number.
32 return sum_of_divisors == num
33
1class Solution {
2 public boolean checkPerfectNumber(int num) {
3 // If the number is 1, it's not a perfect number by definition
4 if (num == 1) {
5 return false;
6 }
7
8 int sumOfDivisors = 1; // Start with 1 since it's a divisor of every number
9
10 // Loop through possible divisors from 2 to sqrt(num)
11 for (int i = 2; i * i <= num; ++i) {
12 // If i is a divisor of num
13 if (num % i == 0) {
14 sumOfDivisors += i; // Add the divisor
15 // Add the complement divisor only if it's different from i
16 if (i != num / i) {
17 sumOfDivisors += num / i;
18 }
19 }
20 }
21
22 // A perfect number equals the sum of its divisors (excluding itself)
23 return sumOfDivisors == num;
24 }
25}
26
1class Solution {
2public:
3 // Function to check if a number is a perfect number
4 bool checkPerfectNumber(int num) {
5 // A perfect number must be greater than 1 because the sum starts from 1
6 if (num == 1) return false;
7
8 // Initialize sum of divisors as 1, considering 1 is always a divisor
9 int sumOfDivisors = 1;
10
11 // Iterate over possible divisors from 2 up to the square root of num
12 for (int divisor = 2; divisor * divisor <= num; ++divisor) {
13 // Check if divisor is a factor of num
14 if (num % divisor == 0) {
15 // Add the divisor to the sum
16 sumOfDivisors += divisor;
17
18 // If the divisor is not the square root of num, add its counterpart
19 if (divisor != num / divisor) sumOfDivisors += num / divisor;
20 }
21 }
22
23 // Check if the sum of divisors equals the original number
24 return sumOfDivisors == num;
25 }
26};
27
1// Function to check if a number is a perfect number
2function checkPerfectNumber(num: number): boolean {
3 // A perfect number must be greater than 1 because the sum starts from 1
4 if (num === 1) return false;
5
6 // Initialize sum of divisors as 1, considering 1 is always a divisor
7 let sumOfDivisors: number = 1;
8
9 // Iterate over possible divisors from 2 up to the square root of num
10 for (let divisor = 2; divisor * divisor <= num; divisor++) {
11 // Check if divisor is a factor of num
12 if (num % divisor === 0) {
13 // Add the divisor to the sum
14 sumOfDivisors += divisor;
15
16 // If the divisor is not the square root of num, add its counterpart
17 if (divisor !== num / divisor) sumOfDivisors += num / divisor;
18 }
19 }
20
21 // Check if the sum of divisors equals the original number
22 return sumOfDivisors === num;
23}
24
Time and Space Complexity
Time Complexity
The time complexity of the provided code is O(sqrt(n))
. This is because the loop runs from 2 up to the square root of num
. The check for each divisor only includes numbers less than or equal to the square root of num
since any larger divisor would have been encountered as a smaller pair divisor before reaching the square root.
Space Complexity
The space complexity of the code is O(1)
, which means it's constant. This is due to the fact that the code only uses a finite number of variables (s
, i
, num
) that do not depend on the size of the input num
.
Learn more about how to find time and space complexity quickly using problem constraints.
Which algorithm should you use to find a node that is close to the root of the tree?
Recommended Readings
Math for Technical Interviews How much math do I need to know for technical interviews The short answer is about high school level math Computer science is often associated with math and some universities even place their computer science department under the math faculty However the reality is that you
LeetCode Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way we
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https algomonster s3 us east 2 amazonaws com recursion jpg You first
Want a Structured Path to Master System Design Too? Don’t Miss This!