Quantcast
Channel: GameDev.net
Viewing all articles
Browse latest Browse all 17825

Advanced Python Part Two: Utilizing Recursion

$
0
0
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.

Article Update Log


July 14 2013: Released Article

Viewing all articles
Browse latest Browse all 17825

Trending Articles