This question regarding exercise 5.5 from Think Python 2 has been asked and answered already, but I still don't understand how this function works. Here's the function in question:
def draw(t, length, n):
if n == 0:
return
angle = 50
t.fd(length*n)
t.lt(angle)
draw(t, length, n-1)
t.rt(2*angle)
draw(t, length, n-1)
t.lt(angle)
t.bk(length*n)
I see a recursive loop here:
def draw(t, length, n):
if n == 0:
return
angle = 50
t.fd(length*n)
t.lt(angle)
draw(t, length, n-1)
After the function calls itself a sufficient number of times to cause n == 0, how does it move on to the next step, namely t.rt(2*angle)? Once n == 0, shouldn't the function return None and simply end? If not, where is returning to? The same thing happens again the next time it calls itself, and then the function continues to perform work long after the state n == 0 has been satisfied (twice!). I'm obviously missing a key concept here and would appreciate any light anyone can shed on this topic. Thanks in advance~
The return statement returns execution of the program back to the line where the function that returned was called.
If you're not familiar with what a call stack is, think of it as a stack of functions - whenever something gets called, it's placed on top of the stack and that's what's running. When that returns, it's removed from the stack and so the function that called it (one below in the stack) is now the top, so that's where execution picks up again.
That particular function will return when n == 0 (so that's your base case), so once its recursive call draw(t, length, n-1) - and, by consequence, its subsequent recursive calls, since it won't finish execution until they do - reach that point, it'll move over to the next line.
Take a simpler function foo(sum, n) instead:
def foo(sum, n):
if (n == 0):
return sum
sum += foo(sum, n-1)
sum /= 2
return foo(sum, n-1)
Whenever you call that, it won't move from sum += foo(sum, n-1) until that call and all of the recursive calls made return. This function is structurally comparable to the one you showed and it might help you visualize a little better what's going on.
I've posted a picture explaining this in the following question , for a fast navigation here's my picture :
Related
I am a beginner and I am beginning to learn about 'while' statement loops to perform iteration. However, earlier on I learned about 'if/else' statements and how one can perform recursion using if/else by returning a variable as well. Here's a simple countdown function using while statement:
def countdown(n):
while n > 0:
print(n)
n = n-1
print('Blast off!')
And for comparison, here is a simple countdown function using if/else and recursion.
def countdown(n):
if n > 0:
print(n)
return countdown(n-1)
elif n < 0:
return None
else:
print('Blast off!')
return
As you can see, the two functions do almost exactly the same thing, with the only difference being that the if/else statement accounts for a case where n < 0 and returns a None value, while the 'while' statement simply omits the loop and prints 'Blast off!' even if n < 0 anyway. (If there is a way to factor this in the while statement, I would love to learn, do suggest!)
My question is, seeing how the same thing can be done in if/else statements and while statements and vice versa, I would like to know a case where they are clearly differentiated and one is clearly preferable to the other. Is there a subtle conceptual difference between the two types of statements that I am missing, or are they interchangeable in usage?
They are fundamentally different. The way you have described them, both are the same thing as for loops. (I don't mean to be rude) While is used when you want something to happen as long as or until something else happens. Recursion is used for functions that are based on themselves. (common examples being factorial or the Fibonacci sequence) Often, they behave similarly in the manner you described on small scale problems. When scaled up however, both have their advantages and drawbacks.
TLDR: the functions are inherently different in how they iterate. While they each iterate, they do so based on different conditions and with different use cases.
You are actually right, the logic behind is the same and you can also apply while loop for e.g. Fibonacci sequence. Just the notation of recursion is usually shorter and more elegant.
Your code can be even simplified a bit and then you see that your solutions are kind of similar:
While loop:
def countdown(n):
while n > 0: # Step 1 -> check
print(n)
n = n-1 # Step 2 -> recursive call in while loop
print('Blast off!') # Step 3 -> end ensured by Step 1
Recursion:
def countdown(n):
if n > 0: # Step 1 -> check
print(n)
return countdown(n-1) # Step 2 -> recursive call of the countdown()
else:
print('Blast off!') # Step 3 -> end ensured by Step 1
Significant is that action n-1. You recursively act on a variable or object (Step 2) as long as your condition related to that variable/object is valid (Step 1).
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) ?
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
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
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".
I'm doing a sudoku solver and found this code online, which used a snimilar method to what I was doing, but I don't understand how grid[y][x] = 0 is able to be reached in this code since it calls solve(grid)in the line before.
I've noticed I get the same output if I put a else statement before it (and as well if it has on less indent)
def solve(grid):
for y in range(9):
for x in range(9):
if grid[y][x] == 0:
for n in range(1, 10):
if possible(y, x, n, grid):
grid[y][x] = n
solve(grid)
grid[y][x] = 0 # How is the program able to reach this code if it calls solve(grid) before it's able to be reached?
return
print(np.matrix(grid))
It is a recursive solution. Recursive means that the solve function calls itself. It continues it's code when the program returns from calling.
grid[y][x] = n # current solve changes grid
solve(grid) # call recursively solve
# the function solve has now returned
# and can have changed grid
grid[y][x] = 0 # now the current solve continues
This works because a list like grid is a mutable object which means that the called function can change grid and the caller continues with the changed grid.
There is one condition: there must be some condition inside solve so that after calling itself multiple times the last called solve returns.
Compare it with the well known Fibonacci function:
def F(n):
if n == 0:
return 0
elif n == 1:
return 1
else:
return F(n - 1) + F(n - 2)
print(F(6))
It stops calling itself when n==0 or n==1. Before that it keeps calling itself with n-1 and n-2.
I don't understand what bugs you - it's just a plain ordinary function call. Obviously a function can call another function, else we couldn't do much.
NB : what the name solve points to is resolved at runtime - the only thing happening at compile time here is that the generated byte-code knows it has to resolve the name solve and apply the call op to whatever this name resolves to.
I am trying to write a recursive function that prints from 0 to n, but I have no idea how to do it. I accidentally made one that prints from n to 0 though:
def countdown(n):
print(n)
if n == 0:
return 0
return countdown(n - 1)
I don't know if that helps or not, maybe I can change something in the code to make it go from 0 to n?
You're about 99% there.
Think of your base case and your recursive step - when you hit 0, what do you want to do? When you're still working your way down from n, what do you want to happen?
If you reverse the order in which you print the value, you'll reach your desired result.
def countdown(n):
if n != 0:
countdown(n-1)
print(n)
The reason this works is that recursive calls go on the call stack. As you push calls onto the stack, while your end case isn't met, you'll keep adding more calls until you reach your base case of n == 0, and then you'll exclusively start printing the values.
The other calls will then fall through to the print statement, as their execution has returned to the line after the conditional.
So, the call stack looks something like this:
countdown(5)
countdown(4)
countdown(3)
countdown(2)
countdown(1)
countdown(0)
print(0)
print(1)
print(2)
print(3)
print(4)
print(5)
You almost got it! here's a fixed, simplified version:
def countup(n):
if n >= 0:
countup(n - 1)
print(n)
Notice that:
You don't have to return anything from a recursive function that only prints values
For printing in ascending order, the print statement must be placed after the recursive call
The recursion exits when n < 0, given that we're only printing, there's nothing left to be done afterwards and it's ok to return None (Python's default return value)
UPDATE
It seems that writing a tail recursive solution is all the rage around here :) oh well, here's my shot at it, a simplified and tail-recursive version of #AndyHayden's idea - using the tail call optimization decorator recipe:
#tail_call_optimized
def countup(N, n=0):
print(n)
if n < N:
countup(N, n + 1)
Either way, it works as expected:
countup(5)
=> 0
1
2
3
4
5
You can replace the 0 and the n, and the + with a - to make your recursive countdown function to a recursive countup:
def countup(N, n=0):
print(n)
if n == N:
return
return countup(N, n + 1)
And call it as follows:
countup(3)
#JFSebastian points out this algorithm has the benefit of being O(1) rather than O(n), as discussed in this excellent article about the difference between a linear and iterative recursion, if used with the #tail_call_optimized decorator.
You can do this
def fun(num,n):
if num<n:
print(num)
fun(num+1,n)
n=int(input())
print(fun(0,n+1))
you can try this method as well:
def recursion_print(n):
if n==0:
print(n)
else:
recursion_print(n-1)
print(n)
recursion_print(9)
It should be best answer from my side.I hope u like this
def countdown(n):
if n >0: #this condition if n is greater than 0 untill run the program.
countdown(n-1) #this is recursion (calling itself).
print(n) #print the numbers.
countdown(10) #function arguments.
I'm trying to halt the for loop below once values (x,y) or (z,2) have been returned so that the value i doesn't keep increasing, and simply halts when the if or elif condition is first
def maxPalindrome(theList):
# students need to put some logic here
maxcomplist = theList[:]
maxcomplist.reverse()
control = len(theList) - 1
# exit if maxPalindrome is True
for i in range(control):
if maxcomplist[:] == theList[:]:
x = 0
y = len(theList)
return (x, y)
break
elif maxcomplist[i:control] == theList[i:control]:
successList = theList[i:control]
z = i
w = len(theList) - z - 1
return (z, w)
How can I accomplish this?
As I wrote in a comment already: that function isn't a recursive one at all.
Recursion means, that a function calls itself to complete it's purpose. This call can be indirect, meaning that the function uses helper function that will call the first function again.
But your code doesn't cover both cases.
A recursive function always have a certain architecture:
the first thing after being called should be to test, if the primitive case (or one primitive case among options) has been reached. if so, it returns.
If not it will compute whatever is needed and pass this results to itself again,
untill the primitive case is reached, and the nested function calls will finish in one after the other.
One well-known usage of recursion is the quicksort algorithm:
def quicksort(alist):
if len(alist) < 2:
return alist # primitive case: a list of size one is ordered
pivotelement = alist.pop()
# compute the 2 lists for the next recursive call
left = [element for element in alist if element < pivotelement]#left = smaller than pivotelemet
right = [element for element in alist if element >= pivotelement]#left = greater than pivotelemet
# call function recursively
return quicksort(left) + [pivotelement] + quicksort(right)
So the "stop" must be the return of a primitive case. This is vital for recursion. You cannot just break out somehow.
I don't understand the question - if I get it right, that what you want already happens. If you return, the function stops running.
Some comments in addition to this answer:
As well, I cannot see where the function is called recursively, nor what
exit if maxPalindrome is True
means. (Is this a comment, maybe?)
Besides, the maxcomplist[:]==theList[:] does not make much sense to me, and seem to be a waste of time and memory, and to have this comparison in each iteration loop doesn't make it faster as well.