SLC21 Week4 - Recursia

Hello everyone, welcome to my lastest homework for @sergeyk 's recursia course SLC21 Week4 - Recursia

This time on a complete rush as this was prepared Sunday hours before the deadline, let's get started.


tastatsast.png
Thumbnail from Pixabay


tas1.png

Recursion is a concept in programming where a function is able to call itself in it's current function or by another one, by dividing the big problem into smaller ones. Imagine it like a loop where you have code that keeps running inside it but in this case it's a whole function that runs as long as the condition is met.

A FRACTAL is that type of pattern that no matter how much you zoom in or out. It is related to recursion because this concept of programming is used to generate fractals. Imagine it like a never ending pattern or an infinite one, the repeating process over and over again in generating the pattern will end in a fractal.

Here is a cool fractal example https://www.youtube.com/shorts/XbDusuz9wfU



tas2.png

Now to find the largest value we could simply google them and create a table, but we want to show them via code, at least for a couple of the data types.

Let's start with INT.


image.png

The idea behind this recursive code is to declare a variable that holds current value of n and current value of the factorial fact and with the use of the limits library we can use the MAX value of a data type in our code.

As long as fact is lower than our INT_MAX/(n+1) we increase the n and do another multiply. We use INT_MAX/(n+1) because this is the overflow check, INT_MAX/n is the cap size of our int, INT_MAX/(n+1) is the next value which overflows our int limit.

While running the code above we get this result, Largest n! that fits in int: 12


image.png

Now let's check CHAR, the code is similar, with just a simple change we get the CHAR limit.


image.png

With the following output:


image.png

INT_MAX was replaced by CHAR_MAX to get the results for our second data type.

How would the factorial multiplier look like because I don't know why I haven't used them both in the code above:


image.png

And the output:


image.png

Using this we can reach the largest N for all data types, even though Factorial is not worth being used with all the data types I will still list down all of them.

Data TypeLargest NFactorial of N (N!)
signed char5120
unsigned char5120
int12479,001,600
unsigned int12479,001,600
long202,432,902,008,176,640,000
unsigned long202,432,902,008,176,640,000
long long202,432,902,008,176,640,000
unsigned long long202,432,902,008,176,640,000
float34295,232,799,039,604,140,847,618,609,643,520,000,000,000,000,000
double1707.257413248004443e+306
long double1754Too large to compute in typical systems


tas3.png

The limit being reached before terminating varies based on multiple factors, with some research we have the following things that can change the number of calls counted before terminating a recursive call.

  • System Stack Size - this limits how deep recursion can go, having a too low limit will throw an overflow error and terminate earlier
  • Compiler Settings - depending on the compiler, some might be more optimized towards recursion
  • Operating System/Platform - OS settings can determine the default stack size

These number of recursive calls can be modified in a few ways:

  • Increasing the Stack Size Limit
  • Tail Call Optimization, which optimizes recursive calls
  • Recursion Depth Control, adding an artificial limit for recursion depth calling, an example could be limiting the calls based on a variable directly in your code.

On a short description the main ways to increase/decrease the number of recursion calls are, changing the stack size, modifying compiler settings and changing your recursion logic.


image.png

Let's see for the factorial code mentioned on the previous task how many calls did it make for N = 5.

The calls are like this

factorial(5), this one calls factorial(4), we move to the next call
factorial(4), this one calls factorial(3), we move to the next call
factorial(3), this one calls factorial(2), we move to the next call
factorial(2), this one calls factorial(1), we move to the next call
factorial(1), this one gets to the base case and returns 1

So in the end we have 5 calls here plus the one where the first factorial is called, this gets us to 6 times (5 recursive + 1 original).



tas4.png

Printing the numbers from 1 to n shouldn't be that difficult, imagine a for loop from 1 to n that prints each loop the number, we are going to change that in a recursive function where each call of the function we check if our value is between 0 and n, if true we call the function with n-1 and print the value. Once our function will reach 1 it will trace back to our n value and print each value. The code should be something like this:


image.png

Right after creating the function I remembered that factorial for negative numbers is not possible, and for 0 and 1, it is 1, we'll avoid this in our code anyway, so I placed a quick check for these..

The output for n=7 looks like this:


image.png

While a negative number or 0 would return this:


image.png



tas5.png

Now the other way around. You might ask yourself why would @sergeyk ask you two work on two similar tasks, let's solve the task and get some conclusions after, I'll have to check first.

After investigating what needs to be changed from the task above, it's actually pretty simple, we just have to swap two lines, like this:


image.png

Output:


image.png

The function call and the printf were swapped, changing this order changes the way the recursive function calls itself and prints.

That's why @sergeyk came with these two tasks, for the user to understand how the call stack would work in these two cases, how the structure of recursive calls would print the value, such a small detail would create so different outputs.

  • for 1 to N, the printing takes place after the recursive call, the stack will go from the smallest value upwards
  • for N to 1 the printing takes place before the recursive call, the stack will go from the largest value downwards

This was more about how the Stack grows and shrinks and not about some simple printing of numbers.



tas6.png

Let's do it with a loop first, we need to use to loops inside an if / else statement like this:


image.png

This is the output:


image.png

The idea behind this approach is pretty simple, you pick one of the two values, and you create a loop from 0 to that value where each time to increment the other value by one.

It's like each loop you make, you add +1 to the other value, and because the number of loops equals the value of your other number you add +1 that amount of times.

In this example above I loop through the first FOR the amount of times represented by a, and each time b gets a +1 value. Going through it 7 times, means I add +1 for the 7 loops I do and it's the same result as adding them directly.

Now the thing changes when there's a negative value, in this case we need to substract 1 and make sure we have a positive value in our loop, that's why in my example I added -a in the loop. -(-a) -> +a, like -7 goes to -(-7) and it becomes positive +7.

Another thing that could be added is a check for the numbers if any of them is 0, but using for loop will will make it work even with 0 as value.

Now how can this be approached with recursion.


image.png

The output as it follows:


image.png

With a recursive function the sum gets added much faster because we increment one value and decrement the other. We increment and decrement values until the one chosen by us (this case b) gets to 0, when it gets to 0 it means we did enough incrementation to the other to reach the sum of the 2 numbers.

This is more like a conversion from the 2 loops to recursive function.



tas7.png

Let's start simple and progress each time more.


image.png

The above picture shows a simple print of a string letter by letter using an array, we simply find the string length and loop through it to print it letter by letter, this is the output.


image.png

Now let's assume we don't know the length.


image.png

With the output:


image.png

In this case we don't know the length of the array, now we loop using a while until we reach \0, if we didn't find it we increment the index and continue our search. Once we reach the \0 our print would end as we reached the string end.

Let's see this example with pointers, where we point to the first position of the string and move from there.


image.png

We start from the address of the first character in the string and increment it until we reach \0. When we add +1 we move to the second address in the string, print it and move on, as long as we didn't reach \0 we'll continue this.

P.S: Now I see I forgot to delete the index declaration from the picture, that one isn't needed anymore

This is the output for the code above:


image.png

Now how do we adapt it to a recursive function:


image.png

With the output:


image.png

We are doing it as we did in the previous code using the pointer and moving from one address to the other, but this time after we print the current value we call the same function again but with the current address +1 to get the value of the next char in string. Once we reach \0 the return will terminate the function.



tas8.png

Now we know from the previous tasks that this can easily be achieved by swamping a couple of lines, we saw how to reverse print something in the two tasks we had above. The one to print values from 1 to N and N to 1.

So our new code should look like this, I will keep the print of each char on a new line to show how we move from one to another:


image.png

With it's output:


image.png


That was it for this homework, in the end I'd like to invite @Mojociocio @r0ssi and @titans.

Thank you again @sergeyk for the amazing work and effort put into this course.

Sort:  
Loading...