Project Euler Problem #95

Problem #95 concerns chains formed by repeatedly replacing a number with the sum of its proper factors. The question reads:

Project Euler Problem 95: Amicable chains
The proper divisors of a number are all the divisors excluding the number itself. For example, the proper divisors of 28 are 1, 2, 4, 7, and 14. As the sum of these divisors is equal to 28, we call it a perfect number.
Interestingly the sum of the proper divisors of 220 is 284 and the sum of the proper divisors of 284 is 220, forming a chain of two numbers. For this reason, 220 and 284 are called an amicable pair.
Perhaps less well known are longer chains. For example, starting with 12496, we form a chain of five numbers:
12496 → 14288 → 15472 → 14536 → 14264 (→ 12496 → ...)
Since this chain returns to its starting point, it is called an amicable chain.
Find the smallest member of the longest amicable chain with no element exceeding one million.

My solution for this problem involves a ton of brute force. Here is my solution:

Solution #1: Brute Force Approach

First, we determine the sum of the proper factors of every number less than 1,000,000 by iterating through the possible divisors less than 500,000 and adding each divisor to every proper multiple of it. Next, we start from 1,000,000 and work down, checking each number for periodic behavior of amicable chains. We can perform this task dynamically, so numbers that are part of larger chains do not need to be checked multiple times. Here is an implementation of this approach in Python 2.7:

 '''
 Author: Walker Kroubalkian
 Sieve Approach to Project Euler Problem #95
 '''
 
 import time
 
 def projectEulerProblemNinetyFive(n):
     properSums = [0]*(n+1)
     for c in range(1,n/2+1):
         for d in range(2*c,n+1,c):
             properSums[d]+=c
     maxLoop = 5
     minNumber = 12496
     loopLengths = [-1]*(n+1)
     for e in range(n,0,-1):
         if loopLengths[e]==-1:
             t = 0
             f = properSums[e]
             loops = True
             found = [e]
             if(f!=e):
                 while(f!=e):
                     t+=1
                     if(f>n):
                         for x in found:
                             loopLengths[x] = 0
                         loops = False
                         break
                     if loopLengths[f]!=-1:
                         for x in found:
                             loopLengths[x] = loopLengths[f]
                             loops = False
                             break
                     if f in found:
                         a = found.index(f)
                         for x in found:
                             loopLengths[x] = len(found)-a
                         found = found[a:]
                         break
                     found.append(f)
                     v = properSums[f]
                     if(v==f or v==0):
                         for x in found:
                             loopLengths[x] = 0
                         loops = False
                         break
                     f = v
                 if loops:
                     for x in found:
                         loopLengths[x] = t
                     if t>maxLoop:
                         maxLoop = t
                         minNumber = found[0]
                         for x in found:
                             if x<minNumber:
                                 minNumber = x
                     elif t==maxLoop:
                         for x in found:
                             if x<minNumber:
                                 minNumber = x
                 else:
                     for x in found:
                         loopLengths[x] = 0
     return minNumber

 start = time.time()
 print projectEulerProblemNinetyFive(1000000)
 print ("--- %s seconds ---" % (time.time()-start))
 
 '''
 Prints
 
 14316
 --- 5.02517795563 seconds ---
 
 for input of n = 1000000.
 ''' 

And with that, we’re done. That took over 5 seconds to run. There must be a faster way to search for loops then the method I used. I definitely want to return to this problem at some point.

Thanks for reading! See you tomorrow.

Published by Walker Kroubalkian

My name is Walker Kroubalkian. I really enjoy math, computer science, and hiking.

Leave a comment

Design a site like this with WordPress.com
Get started