Easy Tutorial: Computer Programming for DUMMIES (recursion made simple!)

in programming •  8 years ago  (edited)

Hey guys, this is part 12 of my programming tutorials! This tutorial is in C++. If you want to catch up on any of my tutorials, here they are:

Part 1: Hello World!

Part 2: Variables

Part 3: Functions

Part 4: if(), else, else if()

Part 5: Loops

Part 6: Arrays

Part 7: Basic Input/Output

Part 9: Sorting Arrays

Part 10: Random Numbers

Part 11: Colored Text in your Terminal

This tutorial is about recursion. A recursive function is a function that calls itself! This can get confusing, but we will walk through a simple example.

Factorial

The factorial function is one of the easiest recursive functions to write. Here is the algorithm:

If you didn't know already, you simply multiply an integer by all of the positive integers less than it.

1! = 1
2! = 2 * 1
3! = 3 * 2 * 1
4! = 4 * 3 * 2 * 1
5! = 5 * 4 * 3 * 2 * 1

Let's look at the program I wrote and decipher it:

#include<iostream>
using namespace std;

unsigned int fact(unsigned int num)
{
    if(num == 1)
        return 1;

    return num * fact(num - 1);
}

int main()
{
    unsigned int num;
    cout << "Enter a integer from 1 to 33: ";
    cin >> num;
    if(num < 1 || num > 33)
        cout << "Invalid number.\n";
    else
        cout << "The factorial of " << num << " is: " << fact(num) << endl;

    return 0;
}

main():

  • "num" is an unsigned int (no negatives) to hold the number we are taking the factorial of
  • prompt the user and scan for the number
  • if the number is less than 1 or greater than 33, print an error message (max unsigned int is smaller than 34!)
  • if it is within the correct range, then print "The factorial of (num) is: (num!)" -- I called the function "fact()" on this line also

fact()

  • if "num" is 1, then simply return 1 (1! = 1)
  • for every other number, call fact() again, except with one less than "num" and multiply it by "num"

You may still not see how this works, so here is a more thorough example:

  • Say we enter 3.
  • fact(3) is called
  • 3 does not equal 1
  • "return num * fact(num - 1);"
  • The function needs to calculate "fact(2)" before it can return a number
    • fact(2) is called
    • 2 does not equal 1
    • "return num * fact(num - 1);"
    • The function needs to calculate "fact(1)" before it can return a number
      • fact(1) is called
      • 1 equals 1, so return 1
    • return 2 * 1 (= 2)
  • return 3 * 2 (= 6)

3! = 6
... Each tab represents the scope of each function call (what "fact(3)" sees, one tab for what fact(2) sees, and two tabs for what fact(1) sees).

Here is some sample output of the program (user input in bold):


[cmw4026@omega test]$ g++ fact.cpp
[cmw4026@omega test]$ ./a.out
Enter a integer from 1 to 33: 4
The factorial of 4 is: 24
[cmw4026@omega test]$ ./a.out
Enter a integer from 1 to 33: 7
The factorial of 7 is: 5040
[cmw4026@omega test]$ ./a.out
Enter a integer from 1 to 33: 1
The factorial of 1 is: 1
[cmw4026@omega test]$ ./a.out
Enter a integer from 1 to 33: 34
Invalid number.
[cmw4026@omega test]$ ./a.out
Enter a integer from 1 to 33: 0
Invalid number.
[cmw4026@omega test]$ ./a.out
Enter a integer from 1 to 33: 33
The factorial of 33 is: 2147483648
[cmw4026@omega test]$


I hope this was helpful! Leave any suggestions in the comments!

Authors get paid when people like you upvote their post.
If you enjoyed what you read here, create your account today and start earning FREE STEEM!
Sort Order:  

This post has been linked to from another place on Steem.

Learn more about linkback bot v0.4. Upvote if you want the bot to continue posting linkbacks for your posts. Flag if otherwise.

Built by @ontofractal

You should explain why recursion may not always yield the most optimal algorithm. A good example is if you use recursion for computing the Fibonacci numbers.

I think someone would need to know what Big Oh is before they understand what you said. I could post about that, but I'm not sure how many people will read past the first few sentences, haha! You want to give a brief summary of that for the comments?

You don't need to explain Big O in the context of recursion and computing Fibonacci numbers. I was referring to how the function call stack will compute the same Fibonacci value multiple times.

For example, if we define F(n) = F(n-1) + F(n-2) with the base case of F(0) = 1, F(1) = 1, then F(4) = F(3) + F(2).

But then, F(3) calls F(2) + F(1) and F(2) calls F(1) + F(0), and we can easily see that F(2) is placed on the stack twice, F(1) is placed on the stack 3 times, and F(0) is placed on the stack twice.

A more efficient algorithm would be to store all pre-computed values in an array of size n + 1. Then, as you iteratively add the correct Fibonacci value to the array, you will note that you only compute each Fibonacci number once instead of multiple times as in the recursive algorithm. Consider the following pseudocode :

std::vector<int>(n+1) fibs;
fibs[0] = 1;
fibs[1] = 1;
for (int i = 2; i < fibs.size(); ++i){
fibs[i] = fibs[i-1] + fibs[i-2]
}

Now, we easily note that the computation for each fibs[i] is done only once. Comparing with the recursive algorithm, and it's easy to see that the iterative algorithm outperforms the recursive one.

There you go -- no need for explaining Big O, ;).

That's basically a like hash table right? You have an array, then a hash function that will give you the right value every time.

Why would you need a hash table?

I only used a stl vector for the contatiner because I didn't want to deal with dynamically allocated arrays of a particular size and then using memory allocation / deallocation in the pseudocode. I could've said

int fibs[n+1];

but ... there's something really dissatisfying about doing that when n is not known at run time ...

But if you mean that the hash function is the trivial

hash(i) = i

function and that you are saying that

fibs[hash(i)] <- F(i) where F(i) is the i'th Fibonacci number, then, yes, I suppose you could do that.

No, I said it is like a hash table... I didn't say that we need a hash table! What I meant is that the array is like a hash table that holds all of the Fibonacci numbers, and calling fibs[x] is like the hash function for it. The more I try to explain this, the less this resembles a hash table, so I'm going to stop right there, haha! It just reminded me of a hash table because you can look things up very quickly (just like you can look up each Fibonacci number very quickly because they are already calculated in an the array).

Referring to what you said about optimal algorithms, not Big Oh.

Now I can hack DAO?