Sunday, February 23, 2014

We must go deeper!

Recursion seems to be a very big theme in this course, and also one of the things that people seem to have the most trouble dealing with. So what is it?

Well there are 2 (kind of similar) answers, one is how I and many others think of it, and one is the official understanding of what it is.

First the official version, mainly so we can forget about it and move on immediately after. According to Miriam Webster, recursion is:

The determination of a succession of elements (as numbers of functions) by operation on one or more preceding elements according to a rule or formula involving a finite number of steps.
Huh?

Well, let's look at some examples and come to an understanding of what it is ourselves.

The first example I was exposed to, waaaaaaaaay before starting this course is my old friend the factorial. In math, a factorial is essentially a number multiplied by all the successive numbers between itself and 1. For example: 7! = 7x6x5x4x3x2x1, 4! = 4x3x2x1 etc. Factorials are incredibly useful in probability since they represent the amount of ways you can arrange a group of objects.

So how would this look in python?

Well, one way is like this:

def factorial(n):
      total = n
      for num in range(1,n):
            total *= num
      return total

Which certainly works (and one might argue would be a little easier on the ol' computer memory) but we can write a "simpler" *cough* version using recursion. Here's how it would look:

def factorial(n):
      if n == 1:
            return 1
      else:
            return n*factorial(n-1)

So here is the big breakthrough here, we can write functions that call THEMSELVES, which is what recursion is. Factorial is perhaps a bad example, because you might be asking me "Well, why would we bother hurting our brain trying to write that when I can just write the other version that is easier for me to understand?" To which I would reply "so you can be lazy later".

Here is what I mean.

Let's say we have a list of numbers and we want to sort them, and we want to sort them as fast as we possibly can (not exactly but we can pretend for sake of example). We want to use a magical type of sorting called quick sort! But what is quick sort?

Imagine we have a list of items: [1,6,3,4,2,5] and we want to sort them fast, first we will choose a random number called a "pivot", in this case we will choose 3. Then what we do is we make 2 more lists, one that has numbers less than 3, and one that has numbers greater than 3, which in this case would yield:

pivot = [3]
less = [1,2]
more = [6,4,5]

Then we do the same thing to less, and more. So for less we might choose 1 as the pivot, so only 2 gets put into more and nothing gets put into less, and for more if we choose 5, 4 gets put into less etc. Then when it is all done we put it together, so we would have something like:

less' less = []
less' pivot = [1]
less' more = [2]

Added up becomes [1,2] and:

more's less = [4]
more's pivot = [5]
more's more = [6]

Added up becomes [4,5,6]

So then we have, originally:

less = [1,2]
pivot = [3]
more = [4,5,6]

And added up is [1,2,3,4,5,6], our sorted list!

Written in non-recursive code this is a bit of a nightmare, as you would have to have a bunch of variables to account for all possible permutations of the six items so that we can quick sort them with a bunch of nested if and else statements. Then if we passed a list with 7 items, suddenly it wouldn't work consistently anymore, and we would have to add more nested if and else statements.

Talk about a lot of work! Luckily for us we can be lazy bums and write this recursively and save ourselves the time. Here's how that would look.

def quicksort(list):
      if len(list) <= 1:
            return list
      else:
            pivot = [list[0]]
            more = []

            less = []
            for number in list[1:]:
                  if number >= pivot[0]:
                        more.append(number)
                  else:
                        less.append(number)
            return quicksort(less) + pivot + quicksort(more)

Which is actually pretty simple! All it requires is calling our function for the smaller lists until we end up with a list of one/no items, which is already sorted for us :-).

Thursday, February 20, 2014

A Demonstration of Recursion

A Demonstration of Recursion

Project Euler

What is the easiest way to study for a programming exam?

Program. It's as simple as that! So after finishing up assignment 2 part 1 (which seems unsettlingly easy) I decided to dive into some programming problems in order to practice some of the concepts I will need to know for the test. These problems are courtesy of a website called Project Euler, which is essentially a huge database of math problems that (mostly) require a computer to solve.

Problem 1, Square Root Convergents:
It is possible to show that the square root of two can be expressed as an infinite continued fraction.
√ 2 = 1 + 1/(2 + 1/(2 + 1/(2 + ... ))) = 1.414213...
By expanding this for the first four iterations, we get:
1 + 1/2 = 3/2 = 1.5
1 + 1/(2 + 1/2) = 7/5 = 1.4
1 + 1/(2 + 1/(2 + 1/2)) = 17/12 = 1.41666...
1 + 1/(2 + 1/(2 + 1/(2 + 1/2))) = 41/29 = 1.41379...
The next three expansions are 99/70, 239/169, and 577/408, but the eighth expansion, 1393/985, is the first example where the number of digits in the numerator exceeds the number of digits in the denominator.
In the first one-thousand expansions, how many fractions contain a numerator with more digits than denominator?
So to solve this I need to have some way to keep track of numerators and denominators, to reduce successive fractions, to check if the numerator has more digits than the denominator, and to find the next iteration of the continued fraction expansion.

So what I did was I made a Fraction class, which has two attributes: a numerator, and a denominator. Then I noticed that for every iteration, we add 1 to the entire thing, put it as the denominator of a fraction (which convieniently is a reciprocal, and then just add 1 again. As an example, if we have our first iteration, which is 3/2 and we add 1 to it we get 5/2, the reciprocal of that is 2/5, and then 1 added to that is 7/5 which is our second iteration. Then we do it again, add 1 to get 12/5, reciprocal is 5/12, add 1 to get 17/12 which is our third iteration. So all I did was make a new fraction by adding the denominator to the numerator, switching the variables, and then adding the denominator in the new fraction to the numerator.

Then all that was needed was to make a simple list comprehension that did all of these fraction expansions and reduced them on every successive iteration. Followed by a filtering of the list by reading the length of the numerator.

This gives the final answer of 153.

Problem 2, Multiples of 3 and 5:
If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23.
Find the sum of all the multiples of 3 or 5 below 1000.
This is the very first problem on the site, which is incredibly easy to solve however in order to practice the concepts, it makes for a good problem since you can write it in one line.

The natural thought process is that to solve it we must iterate through all the numbers from 1 to 999, take out the ones that are multiples of 3 or 5, and then sum all of these together. The way I originally solved it was with an accumulator, and the code looked something like this:

total = 0
for num in range(1, 1000):
        if num%3 == 0 or num%5 == 0:
                total += num
print(num)

So how could we do this in one line? Well with list comprehensions of course! To get a list with all of the numbers that have this property, we can write it like this:

[num for num in range(1, 1000) if num%3 == 0 or num%5 ==0]

So all we need to do is sum them up, and print them, which gives us the one-line beauty:

print(sum([num for num in range(1, 1000) if num%3 == 0 or num%5 ==0]))

Which gives us the final answer of 233168.

Problem 3, Largest Prime Factor:

The prime factors of 13195 are 5, 7, 13 and 29.
What is the largest prime factor of the number 600851475143 ?
So, can this one be written in one line? Let's first think of what we can do to solve this.

Every number can be divided into prime factors, for example 10 can be written as 2x5. The natural thing to do would be to iterate over the prime numbers, and if they divided our number, we divide our number by that, and add that factor to the list. We would then stop iterating when we get to a number that is higher than the number ours has become, and the number we are left over with is in fact the largest prime factor.

Now the truth is, with this implementation, we CANNOT write it in one line, since it requires a variable to be set to 600851475143 for us mess with among other factors. However we can actually write a one line program for this, (although it may not fit on one line on this blog):

print(max([factor for factor in range(2, 600851475143) if (600851475143%factor==0 and len([n for n in range(2, factor) if factor%n == 0]) == 0)]))

What it is basically doing is populating a list of numbers that divide 600851475143 evenly, and which cannot be divided evenly by any number other than 1 and itself (i.e. the list of numbers that divide this number between 2 and itself-1 has a length of 0). After that it simply prints out the largest number in that list, which is by definition our "largest prime factor".

The thing is though, this is a HORRIBLE program, and would take a long time to run even on a supercomputer (In fact, trying it myself it generates a memory error before it gets anywhere). So there is a lesson here, SOMETIMES ONE LINE PROGRAMS ARE NOT THE BEST PROGRAMS.

Anyways, implementing the originally stated algorithm, I got the correct answer of 6857 in a much, much faster time, less than a second.

Sunday, February 9, 2014

Frame-Stewart.

So here is something that always shocks me about university assignments:

Why is it that nobody knows how to use Google?

Because if they did, there would be very little issue with the "Tour" part of our latest assignment. The recursive formula we are provided with in the assignment is known as the "Frame-Stewart Algorithm", and in plain english is as follows.

If you have N cheeses:

1. Move n-i cheeses to some peg that isn't the final peg.
2. Using a 3 tower algorithm, move i cheeses to the final peg
3. Using the same procedure as before, move the cheeses from part 1 to the final peg.

That is all there is to it, and there are even extensive papers on the subject complete with various implementations of the program as well as charts showing that it is optimal at least up to 20 cheeses (regardless of the fact that it is an "unproven" conjecture).

So how did I decide to implement it? Well, I wrote two functions, one to solve recursively for 3 stools based on listed indices (mainly so that they could change depending on what the 4 stools one required), then I wrote a program for 4 stools that uses inputted indices and works recursively the same way.

After it spit out a list of tuples, which directly correspond to moves, I then use that list to apply the moves to a model. I considered just applying the moves directly to the model but I felt like it was easier to animate if the list of moves existed BEFORE we applied them, since all we would then need to do is apply, print, apply, print, apply, print etc.

Perhaps for fun I will try to implement it for n-stools.

Monday, February 3, 2014

On the Number of Triangles in the Sierpinski Triangle.

This is the ever-famous Sierpinski triangle, a relatively simple triangle generated by dividing a starting triangle into 4 equal smaller triangles, then ignoring the centre triangle and performing the same process on the others.

While this was brought up in class, the person sitting beside me brought up an interesting problem: "How many triangles are present in any particular depth of generation?" Which I found very interesting! So here is how we (mostly he) solved it together.

If we think of the triangle as being generated by "Removing" the centre triangle each time, the problem is relatively simple. If we think of the single triangle as generation 0, we have the number of triangles being T_n = 3^n where n is the amount of generations.

The problem gets a bit more complicated when you take the centres into account. In this case we can think of it as 1 when n=0, and then since we are generating but ignoring the centres, we can think of those as the amount of triangles in generation n-1 starting at generation 1, giving us T_n=3^n+T_n-1. However maybe it is possible to get an explicit statement of the equation? Well, let us think about what the equation is for each of the n's and see if we can get a pattern. So:
T_1 = 3^1+3^0
T_2 = 3^2+T_1=3^2+3^1+3^0
T_3 = 3^3+3^2+3^1+3^0...

T_n = sum from 0 to n of 3^n

Can we go further though?

Well:
T_n = 3^0+3^1+...+3^n
3T_n = 3^1+3^2+...+3^(n+1)
3T_n = 3^1+3^2+...+3^(n+1)+3^0-3^0
3T_n = T_n+3^(n+1)-1
2T_n = 3^(n+1)-1
T_n = [3^(n+1)-1]/2

Which gives us an explicit answer that doesn't require knowing T_(n-1) :-)