Advanced Python Part Two: Utilizing Recursion

Published July 14, 2014 by Thomas Hobohm, posted by superman3275
Do you see issues with this article? Let us know.
Advertisement

Python has a surprisingly great ability to perform large recursive operations, allowing for algorithms to be faster and easier to implement. In this article, I discuss the basics of recursion in python and applying divide and conquer to the sorting problem.

The Basics of Recursion

Before I start on advanced recurion topics, I'm going to discuss the basics of programming recursive functions. First, there is the base case. A base case is how your function stops recursively. An example of a recursive function (which lists all the numbers between one and one-hundred) with a base case:

def count(basenumber): 
	if basenumber >= 100: 
		return print int(basenumber) 
	
	count(basenumber + 1) 
	count(1) 

and if it didn't have a base case:

def count(basenumber): 
	print basenumber 
	count(basenumber) 
	count(1) 

The first piece of code prints out all the integers between the number you specify and one-hundred. The second doesn't have the Base Case which terminates the function eventually, making it run forever. To be more clear, this was the base case: if basenumber >= 100: return This makes sure that the function stops at one-hundred. It is important to establish your base case when you are programming a recursive application. This section should (hopefully) explain some recursion basics.

Using "Divide and Conquer" in Recursion

Divide and Conquer is an algorithm design paradigm which easily reduces problems into O(n log n) or O(log n) running time. If you don't know what these are, just know that they are very fast and often better than how fast a "brute-force" algorithm would be. Divide and Conquer means to divide your problem into a large amount of smaller sub-problems, and then solve the original problem using the solutions to these smaller problems.

This often relies on recursion. As an example of Divide and Conquer, there is Merge Sort. If you were to try the "brute-force" solution to sorting numbers (called selection-sort), it would be an O(n^2) algorithm (or quadratic). This is considered slow compared to how fast we can get with divide and conquer (merge sort). Using merge sort, we can get O(n log n). Selection Sort must loop over the list of numbers to sort for every number in the list...

def selectionSort(a): 
    # Go through all positions except the last one 
    # (that one will automatically be correct) 
    for index in range(len(a)-1): 
        value = a[index] 

        # enumerate all (index, value) pairs from the rest of the list 
        # and take the pair with the smallest value 
        min_subindex, min_value = min(enumerate(a[index+1:]), key=lambda x: x[1]) 
        
        if min_value < value: 
            a[index] = min_value 
            a[min_subindex + index + 1] = value 

(That was fairly advanced selection sort code, and it can be confusing) ...while merge sort recursively calls itself, dividing the problem until there is a bunch of lists with only two numbers, and then sorting those lists. It then merges these lists in linear time.

def mergesort(numbers): 
    if len(numbers) == 1: 
        return numbers 
    elif len(numbers) == 2: 
        if numbers[0] < numbers[1]: 
            return numbers 
        else: 
            return [numbers[1], numbers[0]] 
    else: 
        length = len(numbers) 
        
        if length % 2 == 0: 
            list_one = mergesort(numbers[:(length / 2)]) 
            list_two = mergesort(numbers[(length / 2):]) 
        else: 
            list_one = mergesort(numbers[:(length - 1)]) 
            list_two = mergesort(numbers[(length - 1):]) 
        
        result = [] 
        one_traversal = 0 
        two_traversal = 0 
        
        for traversal in range(len(numbers)): 
            if one_traversal == len(list_one) and two_traversal == len(list_two): 
                break 
            elif one_traversal == len(list_one): 
                result.append(list_two[two_traversal]) 
                two_traversal += 1 
            elif two_traversal == len(list_two): 
                result.append(list_one[one_traversal]) 
                one_traversal += 1 
            else: 
                if list_one[one_traversal] > list_two[two_traversal]: 
                    result.append(list_two[two_traversal]) 
                    two_traversal += 1 
                else: 
                    result.append(list_one[one_traversal]) 
                    one_traversal += 1 
        
        return result  

Look at the two base cases at the top. Then look at the merging routine. Merge sort can be confusing, and I recommend you read up on it more.

Interesting Points

Hopefully you understand Python recursion with Divide and Conquer more. I tried to keep the article brief and it may be confusing, so I recommend you ask questions below.

Conclusion

This was a brief introduction to Python algorithms with Divide and Conquer. Try implementing Strassens Matrix Multiplication algorithm (look it up) or an algorithm for counting inversions if you would like to learn more about the divide and conquer paradigm.

Cancel Save
0 Likes 7 Comments

Comments

Dragonsoulj

"Divide and Conquer is an algorithm design paradigm which easily reduces problems into O(n log n) or O(log n) running time. If you don't know what these are, just know that they are very fast and often better than how fast a "brute-force" algorithm would be."

I would probably add that this is called Big O notation and a link so they know what to look up if they want more information: http://en.wikipedia.org/wiki/Big_oh

Overall, I thought it was a decent article. Definitely gets the point of recursion across, however, I noticed you didn't include what recursion actually is, just an example and use.

July 16, 2013 09:12 PM
Gaiiden

... however, I noticed you didn't include what recursion actually is, just an example and use.

That's outside the focus of this article. Plenty of material out there that can help explain recursion. Perhaps some Reference links would be nice though I suppose.

July 17, 2013 01:11 AM
JarNod

I just took an "Analysis of Algorithms" class at my University and the book we used was excellent. http://www.amazon.com/Introduction-Design-Analysis-Algorithms-Edition/dp/0132316811

It does a great job of showing brute force methods and then iterating over the algorithm until you get a lower Big O.

July 17, 2013 10:21 PM
swiftcoder

That's outside the focus of this article. Plenty of material out there that can help explain recursion.

If the focus of this article is not to explain recursion, then I'm not sure what *is* the focus...

It isn't explaining anything about Python, mergesort, or asymptotic notation, either.

July 18, 2013 02:38 AM
wintertime

This reads like a sales pitch throwing together a few examples and unproven claims to support the wrong idea recursion was always better and faster than iterative approaches and would reduce complexity of any and all algorithms to O(n log n) or even O(log n).

It just cant be true.

July 22, 2013 01:53 PM
superman3275
[quote name="wintertime" timestamp="1374501191"]This reads like a sales pitch throwing together a few examples and unproven claims to support the wrong idea recursion was always better and faster than iterative approaches and would reduce complexity of any and all algorithms to O(n log n) or even O(log n). It just cant be true.[/quote] And when did I say that?
July 23, 2013 03:57 AM
ivan.spasov

First off, I want to say that I generally like your Python articles, though I'm not too much into the language (personal preference), despite having some formal experience with VPython.

The one thing about your current article that made me review it as Unclear/Incomplete is really what Dragonsoulj and swiftcoder already covered - you missed out on explaining what recursion actually is. Thing is - you have a "The Basics of Recursion" section without actually having them in. I'm also not particularly keen on your example in that section. There are a lot of more practical and interesting examples out there for this and one that is really abused though pretty clear is the Fibonacci sequence.

That's my opinion on the article, though the last thing with the example might just be splitting hairs.

August 11, 2013 08:19 PM
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!

Python is surprisingly useful for recursive algorithms, which allow for easy O(n log n) or O(log n) solutions to problems which would otherwise be very slow.

Advertisement

Other Tutorials by superman3275

Advertisement