Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Baktracking function which calculates change exceeds maximum recursion depth

I'm trying to write a function that finds all possible combinations of coins that yield a specified amount, for example it calculates all possible way to give change for the amount 2 British pounds from the list of denominations 1p, 2p, 5p,10p,20p,50p,1pound,2pound. I'm stuck with this and can't find the right solution.

I want the main function to be recursive, because I want to understand recursion better. The algorithm must backtrack, if the combination found at some moment exceeds the amount which is to be matched the program should return to previous steps and start from different point.

Thus far I've written a normal (not recursive) function that computes all possible combinations of coins in a given country if each coin is used only once (this is fairly straightforward). I am not trying to find the right combination for a given sum yet, just all possible combinations of coins.

def calcCoins(coins):
    """ 
    returns all possible combinations of coins, when called with 
    [1,2,5,10,20,50,100,200] returns a list of 126 Counters containing 
    for instance Counter{1:1}, Counter{1:1,2:1,5:1}, Counter {50:1,100:1} etc
    """
    i,combs = 1, []
    while i < len(coins):
        for x in combinations(coins,i):
            combs.append(Counter(x))
        i += 1
    return combs

Now I have a clumsy recursive function that accepts a combination and desired amount as arguments and returns all possible ways in which a change equal to this amount can be given.

def findSum(comb,goal,rightOnes):
    if rightOnes == None:
        rightOnes = []
    if sum(comb.elements()) == goal:
        comb_ = Counter(comb)
        if comb_ in rightOnes:
             # probably a cycle, return combinations gathered and exit
             return rightOnes
        rightOnes.append(comb_)
    elif sum(comb.elements()) > goal:
        #this is meant to be backtracking
        return False
    for k in comb:
        comb[k] += 1
        if findSum(comb,goal,rightOnes) != False:
            return findSum(comb,goal,rightOnes)
        else:
            comb[k] = 1
    return rightOnes

The function runs and returns correctly for very small combinations: e.g. for

test2 = Counter({10: 1, 20: 1})
findSum(test2,200,[])

It returns:

 [Counter({10: 18, 20: 1}), Counter({10: 16, 20: 2}), 
  Counter({10: 14, 20: 3}), Counter({10: 12, 20: 4}), 
  Counter({10: 10, 20: 5}), Counter({10: 8, 20: 6}), 
  Counter({20: 7, 10: 6}), Counter({20: 8, 10: 4}), 
  Counter({20: 9, 10: 2})]

But for larger ones, such as

test3 = Counter({1: 1, 2: 1, 10: 1})
test4 = Counter({1: 1, 2: 1, 100: 1, 10: 1}) 

it exceeds the limit of recursion. It runs fine until some moment, prints out partial results but then at some point it exceeds maximum recursion depth.

What are the mistakes I'm making which cuase this function to run amok? Is it something with my implementation of backtracking? Am I omitting some case? How to optimize this function so that it does not exceed maxim recursion depth?

Thanks in advance!

EDIT: Here is the traceback:

   if findSum(comb,goal,rightOnes) != False:
   File "C:\playground\python\problem31.py", line 105, in findSum
   if sum(comb.elements()) == goal:
   File "C:\Python27\lib\collections.py", line 459, in elements
   return _chain.from_iterable(_starmap(_repeat, self.iteritems()))
   RuntimeError: maximum recursion depth exceeded while calling a Python object

and the last partial result, just before the break of the function (called with test3)

[Counter({1: 163, 2: 1, 20: 1, 10: 1, 5: 1}), Counter({1: 161, 2: 2, 20: 1, 10: 1, 5: 1}), 
 Counter({1: 159, 2: 3, 20: 1, 10: 1, 5: 1}), Counter({1: 157, 2: 4, 20: 1, 10: 1, 5: 1}), 
 Counter({1: 155, 2: 5, 20: 1, 10: 1, 5: 1}), Counter({1: 153, 2: 6, 20: 1, 10: 1, 5: 1})]
like image 403
Pawel Miech Avatar asked Jul 08 '13 12:07

Pawel Miech


People also ask

How do you solve maximum recursion depth exceeded?

The “maximum recursion depth exceeded in comparison” error is raised when you try to execute a function that exceeds Python's built in recursion limit. You can fix this error by rewriting your program to use an iterative approach or by increasing the recursion limit in Python.

What is the maximum depth of recursive calls a function?

The maximal number of nested calls (including the first one) is called recursion depth. In our case, it will be exactly n . The maximal recursion depth is limited by JavaScript engine. We can rely on it being 10000, some engines allow more, but 100000 is probably out of limit for the majority of them.

Does backtracking use recursion?

In backtracking, we use recursion to explore all the possibilities until we get the best result for the problem.

How do you overcome maximum recursion depth in Python?

The Python interpreter limits the recursion limit so that infinite recursions are avoided. The “sys” module in Python provides a function called setrecursionlimit() to modify the recursion limit in Python. It takes one parameter, the value of the new recursion limit. By default, this value is usually 10^3.


1 Answers

First of all, as the first answer to this question shows, because of the semantics of Python as a language, recursion isn't a particularly efficient paradigm. However, as is pointed out there, it is possible to use sys.setrecursionlimit(2000). (Or however much you need) I want to stress that this is the "lazy" solution, I strongly recommend using your first (non-recursive) version instead.

like image 186
djpetti Avatar answered Sep 21 '22 15:09

djpetti