0

This is the original code for Fibonacci sequence by using Recursion

def rec(n):
if n<=1:
    return n
else:
    return ( rec(n-1) + rec(n-2))

n=int(input())

Above code gets very slow in around 50th term.

Following code I have returned is also basically a recursion.

n=int(input())
n1,n2,count=0,1,0
def rec(n,n1,n2,count):
    if count<n:
        print(n1)
        nth=n1 + n2
        n1=n2
        n2=nth
        count+=1
        rec(n,n1,n2,count)
rec(n,n1,n2,count)

My question is are both of these approaches follow recursion (like the real recursion) ?

  • 2
    Whats *real recursion*? – Ch3steR Mar 27 '20 at 12:51
  • like the first one. –  Mar 27 '20 at 13:01
  • Both are called *Recursive Functions*. And there's nothing like *Real Recursion*. The 1st function has two recursive calls and second one has one recursive call. What exactly is your definition of *Real Recursion*? I never heard anything like that TBH – Ch3steR Mar 27 '20 at 13:10

3 Answers3

1

Both functions are recursive, but as the last function has the call to itself as the last action in the function, it is also described as tail recursive.

Tail recursive function can easily be converted into loops:

def rec(n, n1=0, n2=1, count=0):
    while count < n:
        print(n1)
        n1, n2, count = n2, n1 + n2, count +1
Terje D.
  • 6,250
  • 1
  • 22
  • 30
  • first function also calls itself in the last action. Doesn't that make the first one also a tail recursive. –  Mar 27 '20 at 13:33
  • @NikhilKaushal, the last action does not mean the list line of code. You see on your last line of code `return (rec(n-1) + rec(n-2))` there are two recursion calls, so the second one must wait for the first one to finish. – DarK_FirefoX Mar 27 '20 at 13:37
  • and after the calls have finished, their return values has to be added together. – Terje D. Mar 27 '20 at 13:42
0

If a function calls itself, it is considered recursive.

The difference between your two implementations is that the first one calls itself approximately 2**n times, the second calls itself about n times.

For n = 50, 2**50 is 1125899906842624. That's a lot of calls! No wonder it takes a long time. (Example: think of the number of times that rec(10) is called when calculating rec(50). Many, many, many times.)

While both of your functions are recursive, I'd say the latter is a "forward iteration", in that you are essentially moving FORWARD through the fibonacci sequence; for your second rec(50), that function only recurses about 50 times.

One technique to make recursive calls faster is called memoization. See "Memoization" on Wikipedia. It works by immediately returning if the answer has previously been calculated... thereby not "re-recursing".

Dan H
  • 14,044
  • 6
  • 39
  • 32
  • One more question, why everyone including websites gives us the example of First code for teaching us recursion. because it is not efficient when we move to like 50+ terms. Second code is pretty much efficient –  Mar 27 '20 at 13:30
0

Well, they are both recursion, because you are calling the function a() inside itself. So, the main difference in both your functions is that there are two recursive calls on the first one and just one on the second one.

So now, on another matter:

Above code gets very slow in around 50th term.

Well, you can do something interesting to do it faster. See, because of the recursive definition of Fibonacci sequence, you are doing the same calculations more than one.

For instance: imagine you are to compute fibonacci(5) so you need to compute fibonacci(4) and fibonacci(3). But now, for fibonacci(4) you need to compute fibonacci(3) and fibonacci(2), and so on. But wait, when you finish computing fibonacci(4) branch you already computed all fibonacci for 3 and 2, so when you go back to the other branch (fibonacci(3)) from the first recursive call, you already have computed it. So, What if there is a way I can store thos calculations so I can access them faster? You can do it with Decorators to create a memoize class (some sort of memory to avoid repeated calculations):

This way we are going to store each computation of fibonacci(k) on a dict and we would check each time before a call if it exists on the dictionary, return if True or else compute it. This way is faster

class memoize:
    def __init__(self, function):
        self.f = function
        self.memory = {}

    def __call__(self, *args):
        if args in self.memory:
            return self.memory[args]
        else:
            value = self.f(*args)
            self.memory[args] = value
            return value

@memoize
def fib(n):
  if n <= 1:
    return n
  else:
    return fib(n-1) + fib(n-2)

r = fib(50)
print(r)

You can see that without memoization it took too long and with memoization it only took 0.263614.

Hope this helps

DarK_FirefoX
  • 887
  • 8
  • 20
  • One more question, why everyone including websites gives us the example of First code for teaching us recursion. because it is not efficient when we move to like 50+ terms. Second code is pretty much efficient. –  Mar 27 '20 at 13:29
  • @NikhilKaushal, think because `Fibonacci` sequence is a classic mathematical recursive example easily understandable. However, maybe they could use it as a way to introduce this sort of techniques later in the website/book. Other recursive problem, but not so simple to grasp for a beginner are [Hanoi Towers](https://en.wikipedia.org/wiki/Tower_of_Hanoi) – DarK_FirefoX Mar 27 '20 at 13:34