Start learning free

LESSON 245 OF 673 · HELPER FUNCTIONS

Perfect Numbers in Python: Divisor Sums with a Helper

Practice Read the full explanation

You get a list of integers nums. Return the perfect numbers among them, in the same order.

A number is perfect when it equals the sum of its divisors smaller than itself. For example, 6 is perfect, because 1 + 2 + 3 = 6. The number 1 is not perfect. It has no smaller divisors, so its sum is 0.

If a perfect number appears twice, it is kept twice. If no number is perfect, the result is empty.

The work is split between two functions. The first one is a helper. It gets one number and returns the sum of its divisors smaller than itself. The second one is the function that is graded. It keeps every number that equals the sum the helper returns for it.

 

Example 1:

Input: nums = [6, 8, 28, 12]
Output: [6, 28]
Explanation: 6 = 1 + 2 + 3.
28 = 1 + 2 + 4 + 7 + 14.
For 8, the sum is 1 + 2 + 4 = 7.
For 12, the sum is 1 + 2 + 3 + 4 + 6 = 16.

Example 2:

Input: nums = [1, 5, 10]
Output: []
Explanation: The sum for 1 is 0.
The sum for 5 is 1.
The sum for 10 is 1 + 2 + 5 = 8.

Example 3:

Input: nums = [496, 6, 6]
Output: [496, 6, 6]
Explanation: 496 is perfect too.
6 appears twice, so it is kept twice.

 

Constraints:

  • nums has 1 to 20 numbers.
  • Every number is between 1 and 10,000.
STEP 1 OF 8
    Ctrl + Enter to check

    PYTHON · HELPER FUNCTIONS

    Perfect Numbers in Python, explained

    Problem overview

    The method perfectNumbers receives a list of integers nums and returns the perfect numbers among them, in the same order. A number is perfect when it equals the sum of its divisors that are smaller than itself. For example, 6 is perfect, because 1 + 2 + 3 = 6. The number 1 is not perfect: it has no smaller divisors, so its sum is 0. A perfect number that appears twice is kept twice, and the result is empty if none qualifies.

    For [6, 8, 28, 12] the answer is [6, 28]. For [1, 5, 10] it is []. For [496, 6, 6] it is [496, 6, 6]. The numbers are between 1 and 10,000, and there are at most 20.

    Approach: a helper for the divisor sum

    The work is split in two. The helper sumOfDivisors returns the sum of the divisors of one number that are smaller than it. perfectNumbers then keeps each number that equals its own helper result.

    The helper tries every candidate from 1 up to n - 1. A candidate d is a divisor when n % d == 0, meaning the division leaves no remainder. The sums for the examples are:

    nsum of smaller divisorsperfect?
    10no
    51no
    66yes
    87no
    108no
    1216no

    Step by step

    1. In sumOfDivisors, start total = 0.
    2. Loop with for d in range(1, n): 1 up to n - 1. The end of a range is excluded, so n itself is left out, as the task needs.
    3. If n % d == 0, add d to total.
    4. Return total.
    5. In perfectNumbers, start with an empty list perfect.
    6. Loop with for num in nums. If self.sumOfDivisors(num) == num, append num.
    7. Return perfect.

    The solution in Python

    class Solution:
        def sumOfDivisors(self, n: int) -> int:
            total = 0
            for d in range(1, n):
                if n % d == 0:
                    total += d
            return total
    
        def perfectNumbers(self, nums: List[int]) -> List[int]:
            perfect = []
            for num in nums:
                if self.sumOfDivisors(num) == num:
                    perfect.append(num)
            return perfect

    Details:

    • For n = 1, range(1, 1) is empty, so the sum stays 0 and 1 is correctly not perfect.
    • For 28 the divisors found are 1, 2, 4, 7 and 14, which add up to 28.
    • For 496 the loop finds 1, 2, 4, 8, 16, 31, 62, 124 and 248, which also add up to 496.
    • Repeated numbers are visited every time, so a repeated perfect number is kept every time.

    Why a helper

    Finding the divisor sum is a job of its own with its own loop and its own total. Putting it in sumOfDivisors means perfectNumbers does not need a loop inside a loop. Its body is one comparison, and the helper can be checked alone: sumOfDivisors(6) is 6, sumOfDivisors(8) is 7 and sumOfDivisors(1) is 0.

    Cost

    The helper runs about n turns for each number, and the largest number is 10,000. With at most 20 numbers, that is about 200,000 checks in the worst case, which is very fast. Nothing cleverer is needed.

    Testing

    numsresult
    [6, 8, 28, 12][6, 28]
    [1, 5, 10][]
    [496, 6, 6][496, 6, 6]

    Common mistakes

    • Using range(1, n + 1). The number itself would be included: for 6 the sum becomes 12, and nothing is ever perfect.
    • Using range(n). It starts at 0, and n % 0 raises ZeroDivisionError.
    • Writing n / d == 0. A divisor is found with the remainder %, not the quotient.
    • Returning inside the loop. The helper would stop after the first divisor.
    • Forgetting self. when calling the helper from the other method.
    Keep going with Python500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free