Programming Recursion (C version)
This is a gentle introduction to programming using recursion. You
need
to read and think along with this essay. It takes about one hour
to hone your skill. Get your favourite C compiler and IDE ready!
Key idea
Recursion needs:
1) terminating case
2) recursive case with increasing/decreasing parameter(s)
Most important thing is choosing the parameters.
Examples
1. Simple recursion, follow a recursive definition. For example,
#include
<stdio.h>
int factorial(n){
if(n == 0) return 1;
else return n *
factorial(n-1);
}
int main(void){
printf("%d\n",factorial(6));
}
The termination case is when n = 0 and the recursive case is n *
factorial(n-1). Try it on your favourite C compiler and see the
result.
2. Summation from a to b
Using a as increasing parameter and check its termination with a = b.
the recursive case is a + sum(a+1, b).
int
sum(int a, int b){
if(a == b) return b;
else return a + sum(a+1,b);
}
int main(void){
printf("%d\n",sum(1,10));
}
3. We can also use an "accumulating parameter" to hold the intermidiate
result. This can turn a recursion into a special from called
"tail-recursion" (which a compiler can make an efficient code). Using s
to hold the intermediate result. We start with s = 0.
Becareful at the termination, the last number needs to be sum too.
int
sum2(int a, int b, int s){
if(a == b) return s+b;
else return sum2(a+1,b,s+a);
}
int main(void){
printf("%d\n",sum2(1,10,0));
}
The "tail-recursion" is more efficient than a general recursion form
because the last recursive call can be turned into a "goto-beginning"
hence saving a function call (and stack space). This makes
recursion as fast as a loop. (This tail-call can be detected and
exploited by any modern compiler).
4. Now we can try another problem. Doing multiplication by
repeat addition. m * n can be calculated by m + m +
... n times.
int
mul(int m, int n){
if(n == 1) return m;
else return m + mul(m,n-1);
}
The recursion will go on for n times.
5. We can do better by rearranging the recurrence
definition. If we do grouping (m + m) = 2m and
2m + 2m + .. It will
need only n/2 times. We repeat doing
this:
4m + 4m + ..
n/4 times.
...
nm ...
n/n or 1 times.
The m * n will need only log_2 n times of loop. This is
much faster.
using p to hold the intermediate result.
int
mul2(int m, int n, int p){
if(n == 1) return p;
else return mul2(m+m, n/2, p+m);
}
Everytime we recurse, m is double, n is half. Of course, it only
works with n = 2^k (n = 2,4,8,16...). It starts with p = m.
The mul2 is faster than mul by logarithmic order. This shows that
improving the "algorithm" can yield very large gain.
6. You can use recursion with an array. The recursion is on
the index. We try the sum again. Summation of all elements
in an array. Let the last element be 0. (it is called a sentinel).
int
ax[] = {11,22,33,44,55,0};
int suma(int i){
if(ax[i] == 0)
return 0;
else return
ax[i] + suma(i+1);
}
int main(void){
printf("%d\n",suma(0));
}
7. Using similar style (recurse on index), you can find a maximum
value in an array.
int
max(int a, int b){
return (a >
b) ? a : b;
}
int maxa(int i){
if(ax[i] == 0)
return 0;
else return
max( ax[i], maxa(i+1) );
}
What it does is it expands into : max( ax[0], max( ax[1], max( ax[2] ...
)
Exercises (to try out your brain)
1. Here is the "copystring" function. A string is an array
of
character terminated with a zero.
char
s1[] = "abcdef";
char s2[100];
/* copy string s1 to s2 */
void copystring(char *s2, char *s1){
int i;
i = 0;
while(s1[i] != 0){
s2[i] = s1[i];
i = i + 1;
}
s2[i] = 0;
}
int main(void){
copystring(s2,s1);
printf("%s\n",s2);
}
Write a recursive version of it.
2. Write a recursive function to reverse a string.
3. Do you know a Fibonacci number. A simple implementation
will be very inefficient. Write a recursive function using an
accumalating parameter will turn that into a very efficient program.
4. Using recusion on index similar to example 6 and 7, write a
program to find whether an array contains a particular number.
5. The fun bit of recursion is when you use it to "build" a
list. It is very elegant. Think how you can write an
"append" function using recursion. It is beautiful.
Prabhas Chongstitvatana
last update 28 Septermber 2011