Flickr Badge

Monday, April 17, 2006

Recursion Part 3: Exercises in tail recursion

The following is a part of a series of articles I had written on recursion.

This part provides some exercises related to the concepts introduced in the previous part.

All Parts:

Recursion Part 1: Introduction to recursion
Recursion Part 2: Tail recursion, Accumulators and Iteration
Recursion Part 3: Exercises in tail recursion
Recursion Part 4: Tree Recursion and Dynamic Programming
Recursion Part 5: Structural and Generative Recursion
Recursion Part 6: References and Further Information


Try out the following problems: Ask any questions/answers in the comments.

Is this tail recursion?

int fibonacci(int n)
{
if (n <= 2) {
return 1;
} else {
return fibonacci(n - 2) + fibonacci(n - 1);
}
}

What about this?

node findRoot(node child)
{
if (NULL == child->parent) {
return child;
} else {
findRoot(child->parent);
}
}

And this?

node findNodeInList(node head, int dataToFind)
{
if (NULL == head) {
return NULL;
} else if (head->data == dataToFind) {
return head;
} else {
return findNodeInList(head->next);
}
}

Try converting the above three programs to iterative versions. Do you find some harder than the others?


Convert this iterative program into a tail recursive version. First convert it to an accumulator based function and then convert that to an ordinary tail recursive version. Hint: First convert the for loop into a while loop.

int sumOfFirstNnumbers(int n)
{
int i = 0, sum = 0;

for (i=1; i<=n; i++) {
sum += i;
}
return sum;
}

How about this one? Assume all numbers are positive (> 0). Hint: The accumulator does not always have to perform some mathematical operation. It can also be used to keep track of the state so far.

int findMax(int *numberArray, int arrayLength)
{
int i = 0, max = 0;

for (i=0; i<arrayLength; i++) {
if (max < numberArray[i]) {
max = numberArray[i];
}
}
return max;
}

Any questions? Comments? Please leave a comment using the comment form below.


This post is a part of the selected archive.

Thursday, April 13, 2006

J2ME notes

Some J2ME notes :
  1. Antenna : Ant tasks for building and packaging J2ME midlets
  2. J2ME performance tips
  3. Remember to catch those OutOfMemoryExceptions! It is all too easy to run out of memory on a contrained device and programmers used to writing code for PCs or servers are just not in the habit of handling out of memory errors

Recursion Part 2: Tail recursion, Accumulators and Iteration

The following is a part of a series of articles I had written on recursion.

This article introduces an important concept in recursion: the tail recursion. We see what tail recursion is, and where it is used. We end the article with the relationship between tail recursion and iteration.

All Parts:

Recursion Part 1: Introduction to recursion
Recursion Part 2: Tail recursion, Accumulators and Iteration
Recursion Part 3: Exercises in tail recursion
Recursion Part 4: Tree Recursion and Dynamic Programming
Recursion Part 5: Structural and Generative Recursion
Recursion Part 6: References and Further Information


Let us get back to the factorial program:


int factorial(int n)
{
if (0 == n) {
return 1;
} else {
return n * factorial(n - 1);
}
}

As we saw in Part 1, the execution of this program goes like this:


factorial(4)
4 * factorial(3)
4 * 3 * factorial(2)
4 * 3 * 2 * factorial(1)
4 * 3 * 2 * 1 * factorial(0)
4 * 3 * 2 * 1 * 1
4 * 3 * 2 * 1
4 * 3 * 2
4 * 6
return 24

In this program, the multiplication with 'n' is the deferred operation (See Part 1 of this series for an introduction on deferred operations). Apart from the deferred operation, there is the recursive call to calculate factorial(n - 1).

Since the recursive call is the last step of the function, this type of recursion is called tail recursion. The name derives from the fact that the recursive call occurs at the end of the function. Oridinary tail recursion has a deferred operation and a recursive call. Pure tail recursion has no deferred operation, it only has a recursive call. The above implementation of the factorial is ordinary tail recursion because it has a deferred operation.

As we saw in Part 1, the deferred operation is carried out when the stack gets popped. Is there any way we can carry out the operation immediately and pass in the value to the function? Take a look at this implementation of factorial:


int factorial(int n)
{
return factorial_acc(1, n);
}

int factorial_acc(int acc, int n)
{
if (0 == n) {
return acc;
} else {
return factorial_acc(n * acc, n - 1);
}
}

Let us trace the execution of this program to calculate factorial(4):


factorial(4)
factorial_acc(1, 4)
factorial_acc(4, 3)
factorial_acc(12, 2)
factorial_acc(24, 1)
factorial_acc(24, 0)
return 24

Notice the difference with the normal factorial implementation? In this version, there are no deferred operations. Instead, the value of the deferred operation is calculated immediately and passed as the first parameter to the recursive function. This parameter is known as the accumulator parameter (in case you were wondering, that is why it is called acc, and the function is called factorial_acc). The role of the accumulator parameter is to accumulate the result of the operations at each step. Note that this version computes the multiplication during a stack push rather than a stack pop. Nothing is done during stack pop, and so this version is pure tail recursion.

Take a look at this iterative implementation of a factorial:


int factorial_iter(int n)
{
int fact = 1, i = n;

while (i > 0) {
fact = fact * i;
i--;
}
return fact;
}

Notice any similarities with the accumulator version? Look at how the variables change with each iteration while calculating factorial(4):


factorial_iter(4)
fact = 1, i = 4
fact = 4, i = 3
fact = 12, i = 2
fact = 24, i = 1
fact = 24, i = 0
return 24

Compare the fact and i variables in the iterative version with the acc and n parameters in the accumulator version. They are identical! In other words, the accumulator version simply implements a while loop using recursion!

So here is the big result: Any ordinary tail recursive program can be converted to a pure tail recursive program with accumulator, which in turn can be converted to a while loop. Thus, ordinary tail recursion and iteration are equivalent, and the above steps give us a way to convert between the two forms!

Because iteration is nothing but a special case of recursion, some languages like Lisp, Scheme and others do not have any iteration methods. There are no for loops, while loops or do-while loops in these languages. All looping is accomplished by using either ordinary or pure tail recursion! These languages have special mechanisms for optimising tail recursion so that they do not increase the stack size.

Languages like C include iterative methods and offer no special support for tail recursion. Since tail recursion is exactly equivalent to iteration, it makes sense to convert all tail recursion into iteration. However, beware of trying to convert non-tail recursion into iteration. As we shall see in later articles in this series, non-tail recursion is a very different beast altogether.


Any questions? Comments? Please leave a comment using the comment form below.


This post is a part of the selected archive.

Wednesday, April 12, 2006

Recursion Part 1: Introduction to Recursion

The following is a part of a series of articles I had written on recursion for a newsletter.

This is the first part of a mini-series of full length articles dealing with recursion. In this, the first part, we talk about some theory behind recursion. This theory will form the foundation and help in understanding the remaining parts of the series. Most of this theory will already be known and it is just a quick overview to refresh your memory.

All Parts:

Recursion Part 1: Introduction to recursion
Recursion Part 2: Tail recursion, Accumulators and Iteration
Recursion Part 3: Exercises in tail recursion
Recursion Part 4: Tree Recursion and Dynamic Programming
Recursion Part 5: Structural and Generative Recursion
Recursion Part 6: References and Further Information


Let us start with the most common example of recursion, calculating the factorial of a number.Here is the C code:


int factorial(int n)
{
if (0 == n) {
return 1;
} else {
return n * factorial(n - 1);
}
}

How does this work? Say we want to calculate factorial(4). Since 4 is not equal to 0, the expression evaluates to 4 * factorial(3). factorial(3) in turn is expanded to 3 * factorial(2). At each point, the previous state of execution is pushed on to the stack. Continuing this way, the expression evaluates to:


4 * 3 * 2 * 1 * factorial(0)

factorial(0) is 1, so we get:


4 * 3 * 2 * 1 * 1
4 * 3 * 2 * 1
4 * 3 * 2
4 * 6
24

The final answer is 24, which is indeed the factorial of 4. Let us summarize the state of execution at every step:


factorial(4)
4 * factorial(3)
4 * 3 * factorial(2)
4 * 3 * 2 * factorial(1)
4 * 3 * 2 * 1 * factorial(0)
4 * 3 * 2 * 1 * 1
4 * 3 * 2 * 1
4 * 3 * 2
4 * 6
24

Note an important point: No multiplications are performed until the stack starts to get popped. Thus, the execution of this function consists of a series of "deferred operations" which are stored in the stack state during the push operation, and which are executed after the pop operation.

Understanding the above point is the key to understanding many concepts in recursion. We will be using it in the other articles in this series.


Any questions? Comments? Please leave a comment using the comment form below.


This post is a part of the selected archive.

Tuesday, April 11, 2006

Elliot's Beach


Elliot's Beach
Originally uploaded by Siddhi.
Karl Schmidt memorial on Elliot's beach in Chennai.

The memorial is named after a Dutch sailor who lost his life rescuing a drowning swimmer.

Wikipedia article : Elliot's beach

Monday, April 10, 2006

Flickr Uploads

I'm uploading to flickr after a while, so just a reminder that the flickr bar that runs across the top of the page should now show some new photographs (finally!)

Friday, April 07, 2006

Really cool rube goldberg video

[via Ned Batchelder] : Google video has a really cool video of various Rube Goldberg machines done by some japanese students. Check it out here. It starts about 1:30 in the video, so you can skip to that point. There is an interruption in the middle which can also be skipped. If Google Videos doesnt work for you, then there are some instuctions on how to view the videos here : http://labnol.blogspot.com/2006/01/download-watch-google-videos-in-any.html

Tuesday, February 28, 2006

ipod packaging parody

If Microsoft made the iPod: microsoft ipod packaging parody [Flash video]

BarCampDelhi

BarCampDelhi looks like it's going to be great. Gaurav has more about it here and here. Too bad I can't attend due to some really important releases coming up.

Wednesday, January 25, 2006

Why program?

via Ta Daa! 4.0:

I love programming. I enjoy the challenge to not only make a working program but do so with style. Programming is like poetry. It conveys a message, not only to the computer, but to those who modify and use your program. With a program, you build your own world with your own rules. You create your world according to your conception of both the problem and the solution. Masterful programmers create worlds with programs that are clear and succinct, much like a poem or essay.

- Programming from the Ground Up, Jonathan Bartlett

Thursday, January 12, 2006

Book Review: Mastery: The Keys to Success and Long-Term Fullfillment

Summary: Mastery is one of the best self-help books I've read in a long time.

Mastery is a unique book among self-help books. Most books in this category promise immediate results. Titles like Learn X in 2 weeks are more common than flies. Mastery is the exact anti-thesis of these books. Mastery focusses on the long term. The core idea in the book is that it takes a very long time to get any good at any skill. Instead of getting frustrated at not seeing the goal, Mastery tells us to enjoy the journey, continuously striving to improve, and one day, a few decades down the road you will be a true master of the skill.

Learning any skill occurs in spurts. Between spurts are plateaus, where in spite of a lot of effort, there seems to be no progress. Then a spurt again, followed by a longer plateau. These plateaus frustrate most of us when learning a skill. There is nothing more discouraging than working and seeing no progress. But accordng to Mastery, there is progress, although we cannot see it. Most of us quit when we reach such a plateau, thinking that we cannot make it. But that is a mistake, because if we persevere, there will eventually be another spurt of progress.

In order to stay on the plateau, Mastery promotes the idea of 'goalless practise'. The idea is to practise, not for any particular goal, but because you enjoy doing the skill. If you can enjoy practise, then you need no additional motivation to continue going when you are on the plateau.

And practise is the key. If there is one thing that differentiates Masters from Amateurs, it is a dedication to practise. So, if you want to become a Master, you will need to practise, not for a couple of days or weeks, but for tens of years.

There is a lot more in the book, including steps to keep you on the path to Mastery. Written by an ex-fighter pilot and aikido master, it contains a lot of zen like philosophy including a focus on long term results and goalless practise.

This is an excellent book. Buy it and read it.

Update: Reading this book reminded me of a similar article by Peter Norvig titled Teach Yourself Programming in Ten Years, where he says

Researchers have shown it takes about ten years to develop expertise in any of a wide variety of areas, including chess playing, music composition, painting, piano playing, swimming, tennis, and research in neuropsychology and topology. There appear to be no real shortcuts: even Mozart, who was a musical prodigy at age 4, took 13 more years before he began to produce world-class music.



This post is a part of the selected archive.

Friday, December 23, 2005

Workstation Ergonomics

"Setting up your computer may seem as easy as pulling up a chair and reaching for the keyboard. Yet positioning or using your computer improperly can lead to all sorts of injuries - from the short term discomfort of headaches to long term (and potentially debilitating) conditions like Carpal Tunnel Syndrome. Many people don't even realize that their pain is computer-related - until they learn proper work habits, and the pain disappears."

Healthy Computing has complete information on proper workstation habits - how to position the keyboard, where to place the monitor, how high the chair should be and lots more. If you want to avoid repetitive strain injuries after ten years, then it is time to apply proper workstation habits right now. For more, see the website: http://www.healthycomputing.com/workstation/index.htm