Search code examples
pythonpython-itertools

Get the index of a maximum value in a set number of iterations in Python's Itertools


(This is Project Euler Problem 14)

I'm trying to diversify my Python skills by using Itertools.

I want to get the index of a maximum returned value in a set number of iterations (1,000,000) from a generator in Python's itertools.

The function works, butt I can't figure out how to get to effectively get the maximum value without storing it in a massive one-million long list which I think could hardly be efficient to this. Is there an intelligent smart way? (edit: Maybe this is the easiest way?)

The current code is that at the moment it stops when the chain reaches 100 which is wrong.

#Project Euler Problem 14
#Which starting number, under one million, produces the longest chain?

import itertools

def length():
    x=1

    while True:
        l=0

        c=x #Create the running integer.
        while c>1:
            if c%2==0:
                #Number is even, divide it by two.
                c=c/2
                l+=1
            else:
                #Number is odd, multiply by three and add one.
                c=3*c+1
                l+=1

        yield l+1   #Add the final one as well and show the chain length.
        x+=1        #Increment


print list(itertools.takewhile(lambda x:x<100,length()))

Solution

  • I approached the problem as follows:

    import functools
    
    def euler_014(max_=1000000):
        longest = max_len = None
        for n in range(1, max_):
            length = len_collatz(n)
            if max_len is None or length > max_len:
                max_len, longest = length, n
        return longest
    
    def memoize(f):
        cache = {}
        @functools.wraps(f)
        def func(*args):
            if args not in cache:
                cache[args] = f(*args)
            return cache[args]
        return func
    
    @memoize
    def len_collatz(n):
        if n == 1:
            return 1
        if n % 2:
            n = (n * 3) + 1
        else:
            n //= 2
        return 1 + len_collatz(n)
    

    Here memoize makes things more efficient by storing previous results for len_collatz, len_collatz tells you the length of the sequence from n to 1 (without actually generating a list of the whole sequence) and euler_014 simply has to keep track of the longest length and the associated value of n. Note that it doesn't keep all of the results - just the length of the longest series so far and the value of n that produced that sequence.