5 Best Ways to Check if Two Numbers are Amicable Numbers using Python

Rate this post

πŸ’‘ Problem Formulation: Amicable numbers are two different numbers so related that the sum of the proper divisors of each is equal to the other number. For example, the number 220 has proper divisors 1, 2, 4, 71, and 142, and their sum is 284; the number 284 has proper divisors 1, 2, 4, 71, and 142, summing up to 220. The objective is to write a Python program to verify if a given pair of numbers are amicable.

Method 1: Using Iteration

This approach involves iterating through numbers from 1 up to one less than the target number to find all divisors and calculating their sum to compare with the second number. It’s straightforward and mirrors the mathematical process of finding amicable numbers.

Here’s an example:

def sum_of_divisors(num):
    return sum([i for i in range(1, num) if num % i == 0])

def are_amicable(num1, num2):
    return sum_of_divisors(num1) == num2 and sum_of_divisors(num2) == num1

print(are_amicable(220, 284))

Output: True

In the code snippet, we define a function sum_of_divisors() that calculates the sum of all proper divisors of a given number. The are_amicable() function uses this to check if two numbers are amicable by comparing the sums of divisors for each.

Method 2: Using Set Operations

This method focuses on creating sets of divisors and then aggregating their values to find the sum. It’s a more Pythonic approach and leverages the set datatype for performance improvements in finding unique divisors.

Here’s an example:

def sum_of_divisors(num):
    divisors = {i for i in range(1, num//2+1) if num % i == 0}
    return sum(divisors)

def are_amicable(num1, num2):
    return sum_of_divisors(num1) == num2 and sum_of_divisors(num2) == num1

print(are_amicable(220, 284))

Output: True

Here, we use a set comprehension to calculate divisors and ensure no duplicates. Again, the function are_amicable() checks for the amicable relationship between two numbers by comparing their divisor sums.

Method 3: Using List Comprehension and a Helper Function

Combining list comprehension for brevity and a helper function for summing divisors, we optimize our code for readability and maintain a clear structure, making the logic easier to follow.

Here’s an example:

def sum_of_divisors(num):
    return sum([i for i in range(1, num//2+1) if num % i == 0])

def are_amicable(num1, num2):
    return sum_of_divisors(num1) == num2 and sum_of_divisors(num2) == num1

print(are_amicable(220, 284))

Output: True

The difference here from method 1 is, we limit our divisor checking to half of the target number, which optimizes the function slightly without changing the semantics. The logic of the are_amicable() function remains unchanged.

Method 4: Using Advanced Math and Functional Programming

The fourth method diverts from brute force and turns to more advanced mathematics. It involves using functional programming concepts like filter and map to deal with divisor collection elegantly.

Here’s an example:

def sum_of_divisors(num):
    return sum(filter(lambda i: num % i == 0, range(1, num//2+1)))

def are_amicable(num1, num2):
    return sum_of_divisors(num1) == num2 and sum_of_divisors(num2) == num1

print(are_amicable(220, 284))

Output: True

We use Python’s filter() function to streamline the finding of divisors and lambda to define an inline anonymous function, then sum them up. This method can be more efficient, but is less readable for beginners.

Bonus One-Liner Method 5: Comprehensive Expression

If we aim for minimalism, a one-liner can be crafted using a generator expression inside the sum() function, encapsulated in a single compound statement.

Here’s an example:

are_amicable = lambda num1, num2: sum(i for i in range(1, num1) if num1 % i == 0) == num2 and sum(i for i in range(1, num2) if num2 % i == 0) == num1
print(are_amicable(220, 284))

Output: True

This one-liner defines an anonymous function are_amicable() using lambda that checks for amicable numbers directly, without separate function declarations. It’s concise but might compromise readability and debuggability.

Summary/Discussion

  • Method 1: Iteration. Simple and easy to understand. Can be slow for large numbers.
  • Method 2: Set Operations. Ensures uniqueness and is slightly faster. Still not the most efficient for very large numbers.
  • Method 3: List Comprehension with a Helper Function. Strikes a balance between clarity and performance. Similar to Method 1 but slightly more optimized.
  • Method 4: Advanced Math and Functional Programming. Streamlines finding divisors with filter and map. Less readable for novices.
  • Method 5: Comprehensive Expression. A compact one-liner suitable for small scripts but at the cost of readability and ease of debugging.