Interesting Project Euler problem
There is one interesting problem to solve from the guys on Project Euler.
I think that maybe there are some problems running a naive approach to the problem on the 2002, but right now, even a inefficient approach can give you results in not much time. I’ve made the following program in Python to measure the differences between a straight approach, and one using recursion and memoization. Looking carefully at the sequences, we realize that each time we get to a previous calculated number, we don’t have to recalculate the sequence again. So, doing it by hand, for the numbers from 1 to 5:
1 : 1 step 2 --> 1 : 2 steps 3 --> 10 --> 5 --> 16 --> 8 --> 4 --> 2 --> 1 : 8 steps 4 --> 2 --> 1: 3 steps (note that is repeated from below) 5 --> 16 --> 8 --> 4 --> 2 --> 1 : 6 steps (again, is repeated)
So, by memoization, we can store the length of previous results, so we don’t have to calculate them again.
from datetime import datetime import operator def inneficient(number): ''' Find the number of steps to 1, inefficiently ''' steps = 1 while number != 1: if number % 2 == 0: number = number / 2 else: number = number * 3 + 1 steps += 1 return steps numbers = {1:1} def efficient(number): ''' Find the number of steps to 1, nefficiently ''' if number in numbers: return numbers[number] else: if number % 2 == 0: result = efficient(number / 2) + 1 else: result = efficient(number * 3 + 1) + 1 numbers[number] = result return result def inefficient_search(times): return max(((number, inneficient(number)) for number in xrange(1, times + 1)), key=operator.itemgetter(1)) def efficient_search(times): return max(((number, efficient(number)) for number in xrange(1, times + 1)), key=operator.itemgetter(1)) def execute(function, times): time1 = datetime.now() result = function(times) time2 = datetime.now() total_time = time2 - time1 print '{0:18}: {1:9} times, time {2}. Result: {3}'.format(function.__name__, times, total_time, result) def main(): ''' Find the longest secuence of numbers ''' SMALL_NUM = 1000 BIG_NUM = 1000000 execute(inefficient_search, SMALL_NUM) execute(efficient_search, SMALL_NUM) execute(inefficient_search, BIG_NUM) execute(efficient_search, BIG_NUM) # Add another result, even bigger, to show that the complexity of the problem is not linear execute(efficient_search, BIG_NUM * 10) if __name__ == '__main__': main()
And the results on my computer:
inefficient_search: 1000 times, time 0:00:00.022727. Result: (871, 179)efficient_search : 1000 times, time 0:00:00.002074. Result: (871, 179)inefficient_search: 1000000 times, time 0:00:31.148715. Result: (837799, 525)efficient_search : 1000000 times, time 0:00:01.822361. Result: (837799, 525)efficient_search : 10000000 times, time 0:00:18.398862. Result: (8400511, 686)
So even the first option doesn’t take too long to run on a modern computer, although, of course, the second one is much faster.
PD: I get to this problem after this post from the blog of S. Lott. Includes a funny cartoon!