Issues with Recursion using Python 3 - python

My question is about what python is doing while recursion works. I get the concept but it seems what's explicit in a loop is implicit in a recursive algorithm. I've seen examples where the recursion will loop through and then step back through to get the answer. Which I don't get. It's like code is happening that I didn't write.
I can't help 'seeing' the return statement return an equation instead of building an equation and returning the answer.
There are some examples of recursion that just make sense but the Fibonacci and factorial type algorithms are confusing. ( disclaimer: I don't want a lesson in fibonacci or factorials ^_^.)
def main():
num = int(input("Please enter a non-negative integer.\n"))
fact = factorial(num)
print("The factorial of",num,"is",fact)
def factorial(num):
if num == 0:
return 1
else:
return num * factorial(num - 1)
main()
if we do !10 I can't help but think it should return the result of each of these equations and loop over that. I'm not sure how python is working through this in memory. Or how it knows that it needs to return the value of 10*9*8*7*6... etc
instead of returning
return 10 * (10 - 1)
return 9 * (9 - 1)
return 8 * (8 - 1)
I know the return calls the function so it can't return anything... but what does it do with the value it already found without overwriting the variables and losing it's place?
Is it staring me right in the face or is there something I just don't know?

Think about it as a math problem. If you know the answer to !9, how would you calculate !10? You simply have to multiply the value of !9 by 10.
That's exactly what the recursive function is doing; it simply expresses the factorial of num as the same thing as num times the factorial of num - 1. The only number for which that doesn't work is 0, but the factorial of 0 is known, it is 1.
So, the factorial of 10 is basically:
10 * factorial(9) ==
10 * 9 * factorial(8) ==
10 * 9 * 8 * factorial(7) ==
10 * 9 * 8 * 7 * factorial(6) ==
10 * 9 * 8 * 7 * 6 * factorial(5) ==
10 * 9 * 8 * 7 * 6 * 5 * factorial(4) ==
10 * 9 * 8 * 7 * 6 * 5 * 4 * factorial(3) ==
10 * 9 * 8 * 7 * 6 * 5 * 4 * 3 * factorial(2) ==
10 * 9 * 8 * 7 * 6 * 5 * 4 * 3 * 2 * factorial(1) ==
10 * 9 * 8 * 7 * 6 * 5 * 4 * 3 * 2 * 1 * factorial(0) ==
10 * 9 * 8 * 7 * 6 * 5 * 4 * 3 * 2 * 1 * 1
Note that each factorial() call gets a new set of variables. No overwriting takes place; it is a whole new, fresh call to the function. The value of num in each call is entirely independent from all the other calls to the function.
If it helps, try using a notebook to trace the function information. Write down the variables on a page, updating them as you step through the code manually. For every new function call, turn over the page and start on the next piece of paper, writing down variables there. You'd write 10 on the first, then 9 (num - 1) on the second page, etc. Returning means taking the returned value, ripping out the page from the notebook and going back a page in the notebook, to update the variables there with that return value.
Python does the exact same thing, using frame objects to track the variables. Each function call is a new frame, and frames are discarded again when a function returns. All the variables for that call are gone with the frame.
Also, Python doesn't care you are re-using the same function here. You could have created 11 separate functions, each with a separate name and a separate num name:
def factorial10(num10):
if num10 == 0:
return 1
else:
return num10 * factorial9(num10 - 1)
def factorial9(num9):
if num9 == 0:
return 1
else:
return num9 * factorial8(num9 - 1)
def factorial8(num8):
if num8 == 0:
return 1
else:
return num8 * factorial7(num8 - 1)
# ...
# etc. all the way to
def factorial0(num0):
if num0 == 0:
return 1
else:
return num0 * factorialminus1(num0 - 1)
and Python would not see any difference between those functions and the original. The exact same work would be executed, but instead of reusing the same function you are using a different function object with identical behaviour. Only the names changed.
So, recursion is just a clever way of chaining together a whole series of function calls. Those function calls are all separate, they don't care about what the local variables of the other functions are doing. Python doesn't have to 'know' anything, it just has to execute the function for you, and when it comes across another function call, execute that function call and use the return value. That that function is the same function or a different one doesn't make any difference.

This is a tough question to answer in a fully authoritative way -- I think different people have different ways of thinking about recursion. My way of thinking about recursion is to force myself to think in a very disciplined, abstract way about what a function is. A function is just a mapping. That's simple in principle, but easy to forget in practice, especially if you're used to thinking in imperative terms -- that is, to thinking about programs as sets of instructions.
Forget about instructions for a moment, and think about the factorial function in its most abstract form:
X Y
--------------------
0 ------> 1
1 ------> 1
2 ------> 2
3 ------> 6
4 ------> 24
5 ------> 120
6 ------> 720
...
Don't worry for now about how to calculate this. Just think about the abstract mapping. Now let's think about how to make a recursive version of this function. What do we need? Well, we need a function that creates a different mapping -- not a mapping from [1, 2, 3, ...] to factorials, but a mapping from one value of Y to the next. In other words (using lower-case now):
x y
--------------------
1 ------> 1
1 ------> 2
2 ------> 6
6 ------> 24
24 ------> 120
120 ------> 720
720 ------> 5040
...
Now let's think about how to calculate this. Immediately a problem appears: 1 maps to 1 the first time and to 2 the second. So we know we're going to have to write a special case to differentiate those two. But for the others, this is pretty simple, right? Just multiply x by its position in the list. So this means for all those parts of the mapping, we need to know just two things: x, and its position in the list:
def factorial_recurrence(x, position):
return x * position
Note that this function now has two arguments, so it's actually a slightly different function than the above:
x, p y
------------------------
1 0 ------> 1
1 1 ------> 2
2 2 ------> 6
6 3 ------> 24
24 4 ------> 120
120 5 ------> 720
720 6 ------> 5040
This shows quite clearly how we can differentiate between the two mappings from 1. Now we just have to come up with a way to get the position information. It just so happens that position is the same as the value of X. So one simple way would be to use a loop. Here we take care of X == 0 by simply setting x to 1 and starting our loop at 1 instead of 0:
def factorial(X):
x = 1
for position in range(1, X + 1):
x = factorial_recurrence(x, position)
return x
Now notice that the value of x here is being passed into factorial_recurrence, and then the result is being saved as x.
So what's really happening here is that the output of the function is being passed back into the function. Here's the big reveal:
That's recursion!
This is, in a crucial sense, already a recursive algorithm. It's just that the representation is inside-out here, and the function also incorporates position information from outside the recursive process. To see what I mean, look at this:
def even_factorial(X):
x = 1
for position in range(2, X + 1, 2):
x = factorial_recurrence(factorial_recurrence(x, position - 1), position)
return x
This gives the same result as factorial for every even value of X. (It gives the result of X - 1 for odd values of X.) And we don't have to stop there. We can do the same thing for every third value of X (breaking out the nesting for clarity):
def third_factorial(X):
x = 1
for position in range(3, X + 1, 3):
x = factorial_recurrence(
factorial_recurrence(
factorial_recurrence(
x,
position - 2
),
position - 1
),
position
)
return x
Now do the same thing for every 4th, every 5th, and so on. If you continue this process, then for any given X, you'll eventually create a function that will return nothing but 1 until you pass X, and then when you pass X, you'll get the factorial of X.
At this point the trick of recursion is simply to realize that we can automate that process of turning the loop inside out by having factorial call itself. Every time factorial is called, it simply nests another factorial_recurrence call inside the last -- unless X is 0, in which case, it returns 1, terminating the sequence of nested calls.
def factorial(X):
if X == 0:
return 1
else:
return factorial_recurrence(factorial(X - 1), X)
So this is kind of a complicated way of thinking about recursion, but its value is that it shows, very clearly, the relationship between the abstraction of recursive functions and their concrete implementation in imperative code.

Related

More efficient way to calculate the nth term

I have the recurrence relation : (n-2)an = 2(4n-9)an-1 - (15n-38)an-2 - 2(2n-5)an-3 with initial conditions being a0 = 0, a1 = 1 and a2 = 3. I mainly want to calculate an mod n and 2n mod n for all odd composite numbers n from 1 up to say 2.5 million.
I have written down a code in Python. Using sympy and memoization, I did the computation for an mod n but it took it more than 2 hours. It got worse when I tried it for a2n mod n. One main reason for the slowness is that the recurrence has non-constant coefficients. Are there more efficient codes that I could use? Or would it help to do this on some other language (which preferably should have an in-built function or a function from some package that can be used directly for the primality testing part of the code)?
This is my code.
from functools import lru_cache
import sympy
#lru_cache(maxsize = 1000)
def f(n):
if n==0:
return 0
elif n==1:
return 1
elif n==2:
return 3
else:
return ((2*((4*n)-9)*f(n-1)) - (((15*n)-38)*f(n-2)) - (2*((2*n)-5)*f(n-3)))//(n-2)
for n in range(1,2500000,2):
if sympy.isprime(n)==False:
print(n,f(n)%n)
if n%10000==1:
print(n,'check')
The last 'if' statement is just to check how much progress is being made.
For a somewhat faster approach avoiding any memory issues, you could calculate the an directly in sequence, while always retaining only the last three values in a queue:
from collections import deque
a = deque([0, 1, 3])
for n in range(3, 2_500_000):
a.append(((8 * n - 18) * a[2]
- (15 * n - 38) * a[1]
- (4 * n - 10) * a.popleft())
// (n - 2))
if n % 2 == 1:
print(n, a[2] % n)
3 2
5 0
7 6
9 7
11 1
[...]
2499989 1
2499991 921156
2499993 1210390
2499995 1460120
2499997 2499996
2499999 1195814
This took about 50 minutes on my PC. Note I avoided the isprime() call in view of Rodrigo's comment.

Basic operation giving different results in Python

I was playing around with few basic operations in Python 3 and came across this
Code 1:
a = 6
b = 3
a = a / 2 * b
print(a)
Output is 9.0
In one of the trainings I have seen that
a = a / 2
can be written as
a /= 2
So I re-wrote my above code as below
Code 2:
a = 6
b = 3
a /= 2 * b
print(a)
But now the output is 1.0.
I was expecting 9.0 as output. Can someone help me understand why code behaves this way?
In the case of a /= 2 * b, you saying that a will be divided by expression after /=.
In other words, your expression could be rewritten as a = a / (2 * b) where a = 6, b = 3
in
a /= 2 * b
the 2 * b is carried out first so if b == 3 it can be rewritten as:
a /= 6
That's because they perform operations in different order.
The first of block of code divides a (6) by 2 and then multiplies that result by b (3)
E.g. (6 / 2) * 3 = 9
However the second block of code does the following:
6 / (2 * 3) = 1
Note, that
a /= 2 * b
is basically
a = a / (2 * b)
The previous code is different. It's not like the above (look at the parentheses)
a = a / 2 * b
This is different than
a = a / (2 * b)
Because the first code interpret it as division and then multiply, while the second code interpret it as multiply (bottom) and then division.
as you can see in the documentatiopn on operator precedence * and / have the same precedence. two operations with the same precedence are executed from left to right:
Operators in the same box group left to right
so your first example is evaluated as
(a / 2) * b
the second as
a / (2 * b)
regarding your comment
a = a / 2 and a /= 2 are always equivalent, right?
for the built-in python numbers you are right.
for your own classes you can make the operations differ. if a is an insance of a custom class a/x will call __truediv__(self, other) while a/=x will call __itruediv__(self, other) as described in the data model.
I'm surprised none of the other answers mention this, but the reason doesn't have anything to do with math, order of operations or implicit parentheses.
x /= y is just another way of saying x.__itruediv__(y). Your second example is not equivalent to your first example, because parameters are evaluated before being passed in to a function call. So, a /= 2 * b is equivalent to a.__itruediv__(2 * b), and naturally, the expression 2 * b must be evaluated before being passed in to __itruediv__.

Python - How to improve efficiency of complex recursive function?

In this video by Mathologer on, amongst other things, infinite sums there are 3 different infinite sums shown at 9:25, when the video freezes suddenly and an elephant diety pops up, challenging the viewer to find "the probable values" of the expressions. I wrote the following script to approximate the last of the three (i.e. 1 + 3.../2...) with increasing precision:
from decimal import Decimal as D, getcontext # for accurate results
def main(c): # faster code when functions defined locally (I think)
def run1(c):
c += 1
if c <= DEPTH:
return D(1) + run3(c)/run2(c)
else:
return D(1)
def run2(c):
c += 1
if c <= DEPTH:
return D(2) + run2(c)/run1(c)
else:
return D(2)
def run3(c):
c += 1
if c <= DEPTH:
return D(3) + run1(c)/run3(c)
else:
return D(3)
return run1(c)
getcontext().prec = 10 # too much precision isn't currently necessary
for x in range(1, 31):
DEPTH = x
print(x, main(0))
Now this is working totally fine for 1 <= x <= 20ish, but it starts taking an eternity for each result after that. I do realize that this is due to the exponentially increasing number of function calls being made at each DEPTH level. It is also clear that I won't be able to calculate the series comfortably up to an arbitrary point. However, the point at which the program slows down is too early for me to clearly identify the limit the series it is converging to (it might be 1.75, but I need more DEPTH to be certain).
My question is: How do I get as much out of my script as possible (performance-wise)?
I have tried:
1. finding the mathematical solution to this problem. (No matching results)
2. finding ways to optimize recursive functions in general. According to multiple sources (e.g. this), Python doesn't optimize tail recursion by default, so I tried switching to an iterative style, but I ran out of ideas on how to accomplish this almost instantly...
Any help is appreciated!
NOTE: I know that I could go about this mathematically instead of "brute-forcing" the limit, but I want to get my program running well, now that I've started...
You can store the results of the run1, run2 and run3 functions in arrays to prevent them from being recalculated every time, since in your example, main(1) calls run1(1), which calls run3(2) and run2(2), which in turn call run1(3), run2(3), run1(3) (again) and run3(3), and so on.
You can see that run1(3) is being called evaluated twice, and this only gets worse as the number increases; if we count the number of times each function is called, those are the results:
run1 run2 run3
1 1 0 0
2 0 1 1
3 1 2 1
4 3 2 3
5 5 6 5
6 11 10 11
7 21 22 21
8 43 42 43
9 85 86 85
...
20 160,000 each (approx.)
...
30 160 million each (approx.)
This is actually a variant of a Pascal triangle, and you could probably figure out the results mathematically; but since here you asked for a non mathematical optimization, just notice how the number of calls increases exponentially; it doubles at each iteration. This is even worse since each call will generate thousands of subsequent calls with higher values, which is what you want to avoid.
Therefore what you want to do is store the value of each call, so that the function does not need to be called a thousand times (and itself make thousands more calls) to always get the same result. This is called memoization.
Here is an example solution in pseudo code:
before calling main, declare the arrays val1, val2, val3, all of size DEPTH, and fill them with -1
function run1(c) # same thing for run2 and run3
c += 1
if c <= DEPTH
local3 = val3(c) # read run3(c)
if local3 is -1 # if run3(c) hasn't been computed yet
local3 = run3(c) # we compute it
val3(c) = local3 # and store it into the array
local2 = val2(c) # same with run2(c)
if local2 is -1
local2 = run2(c)
val2(c) = local2
return D(1) + local3/local2 # we use the value we got from the array or from the computation
else
return D(1)
Here I use -1 since your functions seem to only generate positive numbers, and -1 is an easy placeholder for the empty cells. In other cases you might have to use an object as Cabu below me did. I however think this would be slower due to the cost of retrieving properties in an object versus reading an array, but I might be wrong about that. Either way, your code should be much, much faster with it is now, with a cost of O(n) instead of O(2^n).
This would technically allow your code to run forever at a constant speed, but the recursion will actually cause an early stack overflow. You might still be able to get to a depth of several thousands before that happens though.
Edit: As ShadowRanger added in the comments, you can keep your original code and simply add #lru_cache(maxsize=n) before each of your run1, run2 and run3 functions, where n is one of the first powers of two above DEPTH (for example, 32 if depth is 25). This might require an import directive to work.
With some memoization, You could get up to the stack overflow:
from decimal import Decimal as D, getcontext # for accurate results
def main(c): # faster code when functions defined locally (I think)
mrun1 = {} # store partial results of run1, run2 and run3
# This have not been done in the as parameter of the
# run function to be able to reset them easily
def run1(c):
if c in mrun1: # if partial result already computed, return it
return mrun1[c]
c += 1
if c <= DEPTH:
v = D(1) + run3(c) / run2(c)
else:
v = D(1)
mrun1[c] = v # else store it and return the value
return v
def run2(c):
if c in mrun2:
return mrun2[c]
c += 1
if c <= DEPTH:
v = D(2) + run2(c) / run1(c)
else:
v = D(2)
mrun2[c] = v
return v
def run3(c):
if c in mrun3:
return mrun3[c]
c += 1
if c <= DEPTH:
v = D(3) + run1(c) / run3(c)
else:
v = D(3)
mrun3[c] = v
return v
return run1(c)
getcontext().prec = 150 # too much precision isn't currently necessary
for x in range(1, 997):
DEPTH = x
print(x, main(0))
Python will stack overflow if you go over 997.

Understanding factorial recursion [duplicate]

This question already has answers here:
Understanding recursion in Python
(4 answers)
Closed 3 years ago.
I'm looking over the factorial example of recursion and would just like to make sure that I'm understanding it correctly!
def factorial(n):
if n == 0:
return 1
else:
return n * factorial(n - 1)
Would I be right in saying:
factorial(4) = factorial(4-1) * 4 = factorial(3-1) *3 *4 = factorial(2-1) *2 *3 *4 = factorial(1-1) *1 *2 *3 *4 = 24
because factorial(1-1) = factorial(0) which as the base case shows = 1 and then we multiply by 2, then 3 then 4.
Is that the correct way of looking at it?
Thanks in advance!
Yes it is. But since it's recursion, it works the opposite way. I once had an interviewer explain it to me like this :
Say, for fact(5) :
- fact(5) = 5 * fact(4)
- fact(4) = 4 * fact(3)
- fact(3) = 3 * fact(2)
- fact(2) = 2 * fact(1)
- fact(1) = 1 * fact(0)
- fact(0) = 1
// This is where your condition returns 1.
Now, imagine that the - sign above stands for a return. You basically return whatever is after the - sign. So from the lowest line, 1 is returned. Then, you have 1 returned in fact(1) i.e. 1 * 1. So it happens in a REVERSE cascade like :
= 120
- fact(5) = 5 * 24
- fact(4) = 4 * 6 = 24
- fact(3) = 3 * 2 = 6
- fact(2) = 2 * 1 = 2
- fact(1) = 1 * 1 = 1
- fact(0) = 1
Remember that whenever you work on recursion, everything actually works in reverse. That should really help you breaking any recursion problem down.
This is actually why tail recursion and the related optimization are so important. In memory, each of those calls is delayed and can't return until the calls above it (below in the diagram) finish and return. So a very deep recursive call can cause a stack overflow unless the compiler/interpreter optimize this by turning it into the version in the OP, such that the partial results are evaluated immediately and not delayed. Python does not perform this optimization and so you must be careful with your recursive calls.
This may be helpful
(factorial 4)
(4 * (factorial 3))
(4 * (3 * (factorial 3)))
(4 * (3 * (2 * (factorial 1))))
(4 * (3 * (2 * 1)))
(4 * (3 * 2))
(4 * 6)
(24)
Yes, the way you've described it is what is going on.
One point to note with your code, if you enter a non-integer value for n or a value of n less than 0, it looks like you will be stuck in an infinite loop.
It might be worth adding a check in your code for this:
if not isinstance(n, int):
return None
elif n < 0:
return None

Writing a simple function using while

A Python HOMEWORK Assignment asks me to write a function “that takes as input a positive whole number, and prints out a multiplication, table showing all the whole number multiplications up to and including the input number.”(Also using the while loop)
# This is an example of the output of the function
print_multiplication_table(3)
>>> 1 * 1 = 1
>>> 1 * 2 = 2
>>> 1 * 3 = 3
>>> 2 * 1 = 2
>>> 2 * 2 = 4
>>> 2 * 3 = 6
>>> 3 * 1 = 3
>>> 3 * 2 = 6
>>> 3 * 3 = 9
I know how to start, but don’t know what to do next. I just need some help with the algorithm. Please DO NOT WRITE THE CORRECT CODE, because I want to learn. Instead tell me the logic and reasoning.
Here is my reasoning:
The function should multiply all real numbers to the given value(n) times 1 less than n or (n-1)
The function should multiply all real numbers to n(including n) times two less than n or (n-2)
The function should multiply all real numbers to n(including n) times three less than n or (n-3) and so on... until we reach n
When the function reaches n, the function should also multiply all real numbers to n(including n) times n
The function should then stop or in the while loop "break"
Then the function has to print the results
So this is what I have so far:
def print_multiplication_table(n): # n for a number
if n >=0:
while somehting:
# The code rest of the code that I need help on
else:
return "The input is not a positive whole number.Try anohter input!"
Edit: Here's what I have after all the wonderful answers from everyone
"""
i * j = answer
i is counting from 1 to n
for each i, j is counting from 1 to n
"""
def print_multiplication_table(n): # n for a number
if n >=0:
i = 0
j = 0
while i <n:
i = i + 1
while j <i:
j = j + 1
answer = i * j
print i, " * ",j,"=",answer
else:
return "The input is not a positive whole number.Try another input!"
It's still not completely done!
For example:
print_multiplication_table(2)
# The output
>>>1 * 1 = 1
>>>2 * 2 = 4
And NOT
>>> 1 * 1 = 1
>>> 1 * 2 = 2
>>> 2 * 1 = 2
>>> 2 * 2 = 4
What am I doing wrong?
I'm a little mad about the while loop requirement, because for loops are better suited for this in Python. But learning is learning!
Let's think. Why do a While True? That will never terminate without a break statement, which I think is kind of lame. How about another condition?
What about variables? I think you might need two. One for each number you want to multiply. And make sure you add to them in the while loop.
I'm happy to add to this answer if you need more help.
Your logic is pretty good. But here's a summary of mine:
stop the loop when the product of the 2 numbers is n * n.
In the mean time, print each number and their product. If the first number isn't n, increment it. Once that's n, start incrementing the second one. (This could be done with if statements, but nested loops would be better.) If they're both n, the while block will break because the condition will be met.
As per your comment, here's a little piece of hint-y psuedocode:
while something:
while something else:
do something fun
j += 1
i += 1
where should original assignment of i and j go? What is something, something else, and something fun?
This problem is better implemented using nested loops since you have two counters. First figure out the limits (start, end values) for the two counters. Initialize your counters to lower limits at the beginning of the function, and test the upper limits in the while loops.
The first step towards being able to produce a certain output is to recognize the pattern in that output.
1 * 1 = 1
1 * 2 = 2
1 * 3 = 3
2 * 1 = 2
2 * 2 = 4
2 * 3 = 6
3 * 1 = 3
3 * 2 = 6
3 * 3 = 9
The number on the right of = should be trivial to determine, since we can calculate it by multiplying the other two numbers on each row; obtaining those is the core of the assignment. Think of the two operands of * as two counters, let's call them i and j. We can see that i is counting from 1 to 3, but for each i, j is counting from 1 to 3 (resulting in a total of 9 rows; more generally there will be n2 rows). Therefore, you might try using nested loops, one to loop i (from 1 to n) and another to loop j (from 1 to n) for each i. On each iteration of the nested loop, you can print the string containing i, j and i*j in the desired format.

Categories

Resources