So I have a fairly decent understanding of the concept of recursion, but some implementations really trip me up. Take for instance this simple fibonacci function:
def fib(x):
if x == 0 or x == 1:
return 1
else:
return fib(x-1) + fib(x-2)
I get that this breaks up the fibonacci calculation into smaller more manageable chunks. But how exactly does it come to the end result? What exactly is return returning during the recursive cases? It seems like it is just returning a call to a function that will continue to call the function until it returns 1 -- but it never seems to do any really calculations/operations. Contrast this with the classic factorial function:
def factorial(n):
if n == 1:
return 1
else:
return n * factorial(n)
Here, the function is clearly operating on n, a defined integer, each time, whereas the fibonacci function only ever operates on the function itself until 1 is returned.
Finally, things get even weirder when we bring something like the Merge Sort algorithm into play; namely this chunk of code:
middle = int(len(L)/2)
left = sort(L[:middle], lt)
right = sort(L[middle:], lt)
print(left, right)
return merge(left, right, lt)
left and right seem to be recursively calling sort, yet the print statements seem to indicate that merge is working on every recursive call. So is each recursive call somehow "saved" and then operated on when merge is finally invoked on the return? I'm confusing myself more and more by the second.... I feel like I'm on the verge of a strong understanding of recursion, but my understanding of what exactly return does for recursive calls is standing in my way.
Not understanding how recursive functions work is quite common, but it really indicates that you just don't understand how functions and returning works, because recursive functions work exactly the same as ordinary functions.
print 4
This works because the print statement knows how to print values. It is given the value 4, and prints it.
print 3 + 1
The print statement doesn't understand how to print 3 + 1. 3 + 1 is not a value, it's an expression. Fortunately print doesn't need to know how to print an expression, because it never sees it. Python passes values to things, not expressions. So what Python does is evaluate the expression when the code is executed. In this case, that results in the value 4 being produced. Then the value 4 is given to the print statement, which happily prints it.
def f(x):
return x + 1
print f(3)
This is very similar to the above. f(3) is an expression, not a value. print can't do anything with it. Python has to evaluate the expression to produce a value to give to print. It does that by going and looking up the name f, which fortunately finds the function object created by the def statement, and calling the function with the argument 3.
This results the function's body being executed, with x bound to 3. As in the case with print, the return statement can't do anything with the expression x + 1, so Python evaluates that expression to try to find a value. x + 1 with x bound to 3 produces the value 4, which is then returned.
Returning a value from a function makes the evaluation of the function-call expression become that value. So, back out in print f(3), Python has successfully evaluated the expression f(3) to the value 4. Which print can then print.
def f(x):
return x + 2
def g(y):
return f(y * 2)
print g(1)
Here again, g(2) is an expression not a value, so it needs to be evaluated. Evaluating g(2) leads us to f(y * 2) with y bound to 1. y * 2 isn't a value, so we can't call f on it; we'll have to evaluate that first, which produces the value 2. We can then call f on 2, which returns x + 2 with x bound to 2. x + 2 evaluates to the value 4, which is returned from f and becomes the value of the expression f(y * 2) inside g. This finally gives a value for g to return, so the expression g(1) is evaluated to the value 4, which is then printed.
Note that when drilling down to evaluate f(2) Python still "remembered" that it was already in the middle of evaluating g(1), and it comes back to the right place once it knows what f(2) evaluates to.
That's it. That's all there is. You don't need to understand anything special about recursive functions. return makes the expression that called this particular invocation of the function become the value that was given to return. The immediate expression, not some higher-level expression that called a function that called a function that called a function. The innermost one. It doesn't matter whether the intermediate function-calls happen to be to the same function as this one or not. There's no way for return to even know whether this function was invoked recursively or not, let alone behave differently in the two cases. return always always always returns its value to the direct caller of this function, whatever it is. It never never never "skips" any of those steps and returns the value to a caller further out (such as the outermost caller of a recursive function).
But to help you see that this works, lets trace through the evaluation of fib(3) in more detail.
fib(3):
3 is not equal to 0 or equal to 1
need to evaluate fib(3 - 1) + fib(3 - 2)
3 - 1 is 2
fib(2):
2 is not equal to 0 or equal to 1
need to evaluate fib(2 - 1) + fib(2 - 2)
2 - 1 is 1
fib(1):
1 is equal to 0 or equal to 1
return 1
fib(1) is 1
2 - 2 is 0
fib(0):
0 is equal to 0 or equal to 1
return 1
fib(0) is 1
so fib(2 - 1) + fib(2 - 2) is 1 + 1
fib(2) is 2
3 - 2 is 1
fib(1):
1 is equal to 0 or equal to 1
return 1
fib(1) is 1
so fib(3 - 1) + fib(3 - 2) is 2 + 1
fib(3) is 3
More succinctly, fib(3) returns fib(2) + fib(1). fib(1) returns 1, but fib(3) returns that plus the result of fib(2). fib(2) returns fib(1) + fib(0); both of those return 1, so adding them together gives fib(2) the result of 2. Coming back to fib(3), which was fib(2) + fib(1), we're now in a position to say that that is 2 + 1 which is 3.
The key point you were missing was that while fib(0) or fib(1) returns 1, those 1s form part of the expressions that higher level calls are adding up.
Try this exercise:
What's the value of fib(0)? What's the value of fib(1)? Let's write those down.
fib(0) == 1
fib(1) == 1
We know this because these are "base cases": it matches the first case in the fib definition.
Ok, let's bump it up. What's the value of fib(2)? We can look at the definition of the function, and it's going to be:
fib(2) == fib(1) + fib(0)
We know what the value of fib(1) and fib(0) will be: both of those will do a little work, and then give us an answer. So we know fib(2) will eventually give us a value.
Ok, bump it up. What's the value of fib(3)? We can look at the definition, and it's going to be:
fib(3) == fib(2) + fib(1)
and we already know that fib(2) and fib(1) will eventually compute numbers for us. fib(2) will do a little more work than fib(1), but they'll both eventually bottom out to give us numbers that we can add.
Go for small cases first, and see that when you bump up the size of the problem that the subproblems are things that we'll know how to handle.
If you've gone through a standard high-school math class, you will have seen something similar to this already: mathematicians use what's called "mathematical induction", which is the same idea as the recursion we programmers use as a tool.
You need to understand mathematical induction to really grasp the concept. Once it is understood recursion is simply straightforward. Consider a simple function ,
def fun(a):
if a == 0: return a
else return a + 10
what does the return statement do here? It simply returns a+10. Why is this easy to understand? Of course, one reason is that it doesn't have recursion.;) Why is the return statement so easy to understand is that it has a and 10 available when it is called.
Now, consider a simple sum of n numbers program using recursion. Now, one important thing before coding a recursion is that you must understand how mathematically it is supposed to work. In the case of sum of n numbers we know that if sum of n-1 numbers is known we could return that sum + n. Now what if do not know that sum. Well, we find sum of n-2 terms and add n-1 to it.
So, sumofN(n) = n + sum(n-1).
Now, comes the terminating part. We know that this cant go on indefinitely. Because sumofN(0) = 0
so,
sumofN(n) = 0, if n = 0,
n + sumofN(n-1) , otherwise
In code this would mean,
def sumofN(n):
if n == 0: return 0
return n + sumofN(n-1)
Here suppose we call sumofN(10). It returns 10 + sumofN(9). We have 10 with us. What about the other term. It is the return value of some other function. So what we do is we wait till that function returns. Here, since the function being called is nothing but itself, it waits till sumofN(9) returns. And when we reach 9 + sumofN(8) it waits till sumofN(8) returns.
What actually happens is
10 + sumofN(9) , which is
10 + 9 + sumofN(8), which is
10 + 9 + 8 + sumofN(7) .....
and finally when sumofN(0) returns we have,
10 + 9 + 8 + 7 + 6 + 5 + 4 + 3 + 2 + 1 + 0 = 55
This concept is all that is needed to understand recursion. :).
Now, what about mergesort?
mergesort(someArray) = { l = mergesort the left part of array,
r = mergesort the right part of the array,
merge(l and r)
}
Until the left part is available to be returned, it goes on calling mergesort on the "leftest" arrays. Once we have that, we find the right array which indeed finds the "leftest" array. Once we have a left and right we merge them.
One thing about recursion is that it is so damn easy once you look at it from the right perspective and that right perspective is called mathematical induction
Related
I found a basic code in python to find the numbers of paths you can take in a (m,n) grid if you can only go either down or right.
def gridtraveller(m,n):
if m == 0 or n == 0:
return 0
elif m == 1 or n == 1:
return 1
return gridtraveller(m-1,n) + gridtraveller(m,n-1)
But I dont understand why is this working for two thing:
What does def something (m,n) do ?
And why does here we return the definition ? ( I do understand why we return
m-1 and n-1 , but I don't understant the concepte of a def returning a def)
Thanks to you and sorry english is not my first language.
In Python the def keyword is simply used to define a function, in this case it's the function gridtraveller(m,n). What you're seeing with that last return statement is actually a function returning the value of another function. In this case it's returning the value of another call to gridtraveller, but with different parameter values; this is called recursion. An important part of recursion is having appropriate base cases, or return values that won't end in another recursive call(i.e. the return 0 or return 1 you see).
It can be easier to understand by simply stepping through a few iterations of the recursive calls. If your first function call starts with m = 2 and n = 1, the first call will end with return gridtraveller(1,1) + gridtraveller(2,0). The first call in that statement will then return 1 since either m or n are 1 and the second returns 0 since n = 0 here giving a total result of 1. If larger values of m and n are used it will obviously result in a higher number since more calls to gridtraver(m,n) will happen.
I've been doing Python puzzles and one I have been doing is using a concurrent function to solve the Kempner Function in Python.
The Kempner Function, applied to a composite number, permits to find the smallest integer greater than zero which factorial is exactly divided by the number.
For example:
kempner(6) ➞ 3
1! = 1 % 6 > 0
2! = 2 % 6 > 0
3! = 6 % 6 === 0
kempner(10) ➞ 5
1! = 1 % 10 > 0
2! = 2 % 10 > 0
3! = 6 % 10 > 0
4! = 24 % 10 > 0
5! = 120 % 10 === 0
There are various ways of doing this, and one of the solutions I have seen is this:
def kempner(n, i=1, total=1):
if total % n == 0:
return max(1, i-1)
else:
return kempner(n, i+1, total*i)
I understand the gist of what this is doing, however when I run it through debug mode and see what the variables are doing I can see that when the base condition is reached (if total % n ==0) and return max(1, i-1) is returned then everything in the else clause will continue to run until the function returns to its starting condition (e.g. for kempner(10) then n = 10, i = 1, total = 1). Why does it do that? Surely it should stop its recurrence if the base condition has been reached?
This is a fairly abstract issue and is obviously a blind spot in my knowledge. If anyone has any insight I would be grateful.
Recursive calls are just like any other function call: when they return, they return control back to whatever called them.
Say you have a series of numbered recursive calls:
1 -> 2 -> 3 -> 4
Base Case Reached
If recursive call 3 called recursive call 4, and recursive call 4 ended at the base case, returning from recursive call 4 will take you back to recursive call 3, because 3 called 4. This is just like any other functions call:
def second_func():
print("Inner")
return 5
def first_func():
return second_func()
When you return from second_func, you return control back to first_func, since first_func called second_func. You don't immediately exit from second_func back to main or something else. It's the same with recursive calls. The only difference when dealing with recursion is first_func and second_func are the same function, but that doesn't affect the mechanics of returning.
There is no way (other than using something like exceptions) to exit from the entire call chain at once.
This question already has answers here:
How does the fibonacci recursive function "work"?
(12 answers)
Closed 2 years ago.
# Function for nth Fibonacci number
def Fibonacci(n):
if n<0:
print("Incorrect input")
# First Fibonacci number is 0
elif n==1:
return 0
# Second Fibonacci number is 1
elif n==2:
return 1
else:
return Fibonacci(n-1)+Fibonacci(n-2)
# Driver Program
print(Fibonacci(9))
I am new to programming.
I cant get how this codes finds out the Fibonacci number....
How does the program calculates the value of (n-1) and (n-2)
Your program uses recursion.
Here you can find visualization, which could help you with understanding:
https://observablehq.com/#victormutai/visualizing-recursive-fibonacci-algorithm
Alternative implementation is iterative algorithm. I could be easier to understand
def f(n):
a, b = 0, 1
for i in range(0, n):
a, b = b, a + b
return a
Here is an illustratory diagram from the SICP book.
When Fibonacci(5) is invoked, Fibonacci(5-1) (fib 4) and Fibonacci(5-2) (fib 3) are invoked in return. When Fibonacci(4) is invoked, Fibonacci(4-1) and Fibonacci(4-2) are invoked in return, so on and so forth. This is called a recursion.
Finally, when either Fibonacci(2) or Fibonacci(1) is invoked, its result 1 and 0 is returned directly without further invocation. It is the termination conditions of such recursion.
By the way, as is depicted in the diagram, the multiple times' invocations of fib 3, which is seen as repeated calculation, result in inefficiency. So it is only for demonstrating how recursion works while usually not used to calculate fibs in practice.
return Fibonacci(n-1)+Fibonacci(n-2)
This is a recursion. You should cache values to reduce recalculation.
moreover, there is Binet's formula to calculate it at O(1) time.
https://en.wikipedia.org/wiki/Fibonacci_number
The idea behind that piece of code is recursion. Have a look at this to find out more about it.
To explain how your code works to identify the nth number in the Fibonacci series, first you need to understand how a Fibonacci series is generated.
0, 1, 1, 2, 3, 5, 8, 13, and so on.
Here if you look at the first two numbers it is 0 and 1, these are the base numbers, from there on the next number is found out by the addition of the previous two numbers. Hence the formula.
F(1) = 0
F(2) = 1
F(n) = F(n-1) + F(n-2)
Now if you look at your code the above is exactly depicted here. Any value of n less that 1 will be considered as invalid. A value of 1 returns a 0 and a value of 2 returns a 1. this is exactly the same as we have mentioned above.
Fibonacci(n-1)+Fibonacci(n-2)
Finally for all other cases we find the previous two numbers in the series by using the same rule(function) and sum them to get the desired number.
First, the following is my function.
def countdown(n):
if n <= 0:
return 0.0
else:
print(n)
countdown(n-1)
Second, when I call countdown(0), the result is as below.
>>> countdown(0)
0.0
It means, since 0 <= 0 is true, then do if statement return 0.0.
Next, when I call countdown(3), the result is.
>>> countdown(3)
3
2
1
But I expectation is
>>> countdown(3)
3
2
1
0.0
My question is, why countdown(3) does not return 0.0?
Thank you very much.
Your notion of result mixes up two different things:
something printed by a function, as a side-effect.
something returned by a function, which gets printed by REPL when you call the function in the interactive interpreter, but doesn't get printed otherwise (e.g. if you run the whole program with the python interpreter invoked from the shell).
In your first example, countdown(0) doesn't print anything, but REPL prints the value returned by countdown(0), which is 0.0.
In your second example, countdown(3) ends up calling countdown(2), and so on... but the value from countdown(0) is lost: that's because countdown(1) calls countdown(0) for side-effect only, not returning anything (which is equivalent to returning None).
There are two ways to make countdown match your expectations, when calling it in REPL:
return countdown(n-1) as a final statement;
print 0.0 instead of returning it in the first if branch.
These two variants look the same when run in REPL, but in fact they are different (see the first paragraph above).
You probably didn't want to use return. Instead of return use print().
So your function should be:
def countdown(n):
if n <= 0:
print(0.0)
else:
print(n)
countdown(n-1)
Or simplified (but a bit weird):
def countdown(n):
print(max(0.0, n))
if n > 0:
countdown(n - 1)
As answer to your question I will say: it's because Python shell prints returned object and code doesn't.
Make program which executes your countdown function with value 0 or less.
It won't print anything.
So it's best to not use return for printing things.
EDIT:
You should use return when you (or anyone else) wants to use that value.
Example:
def SUM(a, b, c):
return a + b + c
If you want to use SUM of three numbers you could just write a = SUM(3, 4, 5).
If you used print instead of return a would be None.
Also I think almost all shells prints returned value.
Return printing is like printing values without print, e.g.: if you write 5 + 1 in shell it would print 6, but if it's in function/program it wouldn't do anything.
#Calculates to the index position of a fib number.
def f3(n):
if n < 2:
return n
return f3(n-2) + f3(n-1)
The function only accepts one argument, yet two are being sent in the return, yet, it works! What's happening here?
If I return f3(n-3), the function breaks down. What effect does the concatenation have?
Addition results in a single value.
>>> 1 + 2
3
>>> [1] + [2]
[1, 2]
Python evaluates the expression f3(n-2) + f3(n-1) before returning it, so its actually returning the value of them combined. The same is the case for f3(n-2), its first evaluating n-2 and then passing it as a value to f3().
The number of return arguments has nothing to do with the number of arguments a function takes as input.
The line f3(n-2) + f3(n-1) is returning only one value, the result of calculating f3 for the input n-2 and then adding that value to the result of calculating f3 for the input n-1
In Python, the mechanism for returning multiple values from a function is by packing them inside a tuple and then extracting them at the time of invoking the function (not the case in your question!) For example:
def multivalue(x, y)
return (x, y)
a, b = multivalue(5,10)
# here a holds 5, and b holds 10