Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Could someone explain why this fixes my recursion error?

Tags:

python

I was implementing binary search recursively in Python (I know this is bad) and got a max recursion error with the following code:

def bs_h(items,key,lower,upper):
    if lower == upper:
        return None
    mid = (lower + upper) // 2
    if key < items[mid]:
        return bs_h(items,key, lower, mid)
    else:
        return bs_h(items,key,mid,upper)

def bs(items,key):
    return bs_h(items,key, 0, len(items)-1)

I then changed my parameters and base case like so:

def bs_h(items,key,lower,upper):
    if lower + 1 == upper:
        return None
    mid = (lower + upper) // 2
    if key < items[mid]:
        return bs_h(items,key, lower, mid)
    else:
        return bs_h(items,key,mid,upper)

def bs(items,key):
    return bs_h(items,key, -1, len(items))

This fixes the error, but I am not sure why. Can someone please explain?

like image 585
Dante Tomato Avatar asked Jul 24 '17 17:07

Dante Tomato


People also ask

How do you solve recursive errors?

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 recursion error?

The JavaScript exception "too much recursion" or "Maximum call stack size exceeded" occurs when there are too many function calls, or a function is missing a base case.

How do you stop recursion error in Python?

Try increasing the recursion limit ( sys. setrecursionlimit ) or re-writing your code without recursion. Return the current value of the recursion limit, the maximum depth of the Python interpreter stack. This limit prevents infinite recursion from causing an overflow of the C stack and crashing Python.

How do I fix maximum recursion depth exceeded in Python?

The maximum recursion depth in Python is 1000. You can change the limit by calling sys. setrecursionlimit() method.


4 Answers

Whenever you use recursion (and it is sometimes very useful), you need to be extremely careful about the end conditions.

  1. Does it ever terminate?
  2. If it does, how deep will it go?

At some point during the running of your code, you might have a call like:

bs_h(items, key, 10, 11)

which then leads to:

mid = (lower + upper) // 2
    = (10 + 11) // 2
    = 10
if key < items[10]:
    return bs_h(items, key, 10, 10)
else:
    return bs_h(items, key, 10, 11)

Notice that last statement - it's the same as the entry call. If the program ends up at this point, it'll always act recursively.

Always check how you'll escape from recursion, and btw, check your "new improved version" for this.

like image 84
Glenn Rogers Avatar answered Oct 16 '22 10:10

Glenn Rogers


What fixes your code is this change:

if lower + 1 == upper:
    return None

The change in bs is irrelevant (at least for this purpose; I would actually argue against it).

The reason can be observed from the following experimentation:

>>> (0+0)//2
0

>>> (0+1)//2
0

So, the midpoint does not change once the interval is <= 1 so it follows you should stop at the earliest opportunity, otherwise you will continue looping waiting for midpoint to change which will never occur.

like image 28
AGN Gazer Avatar answered Oct 16 '22 10:10

AGN Gazer


Put a print(lower, upper, mid) statement after your mid = (lower + upper) // 2 statement in your first bs_h example and observe the following:

>>> bs(list(range(10)), 5)
0 9 4
4 9 6
4 6 5
5 6 5
5 6 5
... # repeats until max recursion depth.

Since both (n+n)//2 and (n+n+1)//2 are always the same, you get to a point where you continuously call bs_h with the same lower and upper parameters. You should adjust your subsequent calls to eliminate the boundary conditions (which have already been checked). You also need to return something like the index. The following matches the recursion pattern you want and returns the correct index:

def bs_h(items,key,lower,upper):
    if lower > upper:
        return None
    mid = (lower + upper) // 2
    if key < items[mid]:
        return bs_h(items,key, lower, mid-1)
    elif key > items[mid]:
        return bs_h(items,key,mid+1,upper)
    else:
        return mid #Found! return index

And tested:

>>> for i in range(10):
    print(bs(list(range(10)), i))

0
1
2
3
4
5
6
7
8
9

>>> print(bs(list(range(10)), 4.5))
None

>>> print(bs(list(range(10)), 100000))
None
like image 1
Billy Avatar answered Oct 16 '22 10:10

Billy


Glenn Rogeres got it right. But to make sure things aren't missed,

  • Firstly, its binary search - which means to find a value in a sorted list.
  • you can return the index of the element(in your case, mid variable once the element is found!)
  • after checking if the key is < or > than the items[mid] you can ignore the mid element in your next recursive search(mid-1 or mid+1)

That is,

def bs_h(items,key,lower,upper):
    if lower>upper:return None
    mid = (lower + upper )// 2
    if key < items[mid]:
        return bs_h(items,key, lower, mid-1) 
    elif key > items[mid]:
        return bs_h(items,key, mid+1, upper)
    else:return mid

def bs(items,key):
    return bs_h(sorted(items),key, 0, len(items)-1)
like image 1
Keerthana Prabhakaran Avatar answered Oct 16 '22 11:10

Keerthana Prabhakaran