简体   繁体   中英

Convert recursive function to iterative function

Although this may seem like a duplicate (maybe it is but I haven't found a solution to this version of the problem yet), I don't think it is.

Below is my recursive function which blows python's recursion limit and I'd need to make it iterative instead but I'm having issues seeing how it could be possible.

def countChain(n, cache):
    if cache[n] != -1:
        return cache[n]

    if n % 2 == 0:
        cache[n] = 1 + countChain(n / 2, cache)
    else:
        cache[n] = 2 + countChain((3 * n + 1) / 2, cache)

    return cache[n]

Note that here my cache list has 1 million elements in it... (which is why the recursion kills python). I've seen people use an accumulator to do the job but here I'm not returning directly the result of the recursive call which makes this idea hard to implement.

EDIT

the first recursive call should have been cache[n] = 1 + countChain(n / 2, cache) instead of cache[n] = 1 + countChain(n, cache)

EDIT 2

Someone asked for example data so I will simply type in the whole code (not that long) for a better understanding.

import time
import sys
import math

def main():
    target = int(sys.argv[1])

    start = time.time()
    longest = 0
    answer = -1

    for i in range(int(target/2), target):
        if countChain(i) > longest:
            longest = countChain(i)
            answer = i

    print("Result = ", answer, " in ", (time.time() - start), " seconds")

def countChain(n,cache={1:0}):
    if n not in cache: 
        if n % 2 == 0:
            cache[n] = 1 + countChain(n//2, cache)
        else:
            cache[n] = 2 + countChain((3 * n + 1) // 2, cache)
    return cache[n]

if __name__ == "__main__":
    main()

The usual input is 1000000

Also the else should have been 2 + ...

Three versions: The natural recursive and cached one :

def countchain_recursive_cache(n):
    if n not in cache: 
        if n % 2 == 0:
            cache[n] = 1 + countchain_recursif(n//2)
        else:
            cache[n] = 1 + countchain_recursif(3 * n + 1)
    return cache[n]

A pure iterative one:

def countchain_iterative(n):
    count=0
    while n>1:
        if n%2 == 0 : 
            n //= 2
            count += 1
        else :
            n = (3*n+1)//2
            count += 2
    return count

And a stackless cached version :

def countchain_iterative_cache(n):
    count = 0
    n0=n
    while n not in cache:
            count += 1
            if n%2 == 0 : n //= 2
            else : n = 3*n+1
    count+= cache[n]
    n=n0
    while n not in cache:
            cache[n]=count
            count-=1
            if n%2 == 0 : n //= 2
            else : n = 3*n+1
    return cache[n]

The cache mechanism have no interest if the cache is in the function. it must be global to accelerate further calls.

Here are the timings :

%time  for i in range(1,10**4) : countchain_iterative(i)
cache={1:0}
%time  for i in range(1,10**4) : countchain_iterative_cache(i)
cache={1:0}
%time  for i in range(1,10**4) : countchain_recursive_cache(i)
%time  for i in range(1,10**4) : countChain_morcedist(i)

# respectively :
Wall time: 490 ms
Wall time: 80 ms
Wall time: 54 ms
Wall time: 3.82 s

But the most efficient way if the uncached iterative way if you just want one count chain :

%time countchain_iterative(10**10_000)  
Wall time: 6.37 s
Out[381]: 177856

And finally, I conjecture you will never crash the fast recursive function in a iterative construction :

%time for i in range(1,10**7): countchain_recursif(i)
Wall time: 1min 8s

You can always convert a recursive function to an iterative one by using a stack. Here's how I would do it:

def countChain(n):
    cache = { 1: 0 }
    stack = [n]
    while n not in cache:
      n_curr = stack.pop()

      if n_curr % 2 == 0:
        if n_curr / 2 in cache:
          cache[n_curr] = 1 + cache[n_curr / 2]
        else:
          stack.append(n_curr)
          stack.append(n_curr / 2)
      else:
        if (3 * n_curr + 1) / 2 in cache:
          cache[n_curr] = 3 + cache[(3 * n_curr + 1) / 2]
        else:
          stack.append(n_curr)
          stack.append((3 * n_curr + 1) / 2)

    return cache[n]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM