Understanding factorial recursion [duplicate] - python

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

Related

Why print(5 ** 2 ** 0 ** 1) = 5 in Python?

Can somebody explain me why Why print(5 ** 2 ** 0 ** 1) = 5 in Python?
I am trying to learn the language and somehow I am not quite sure how this math is done.
Thank you in advance.
Because, like many languages, the exponentiation operator binds right-to-left:
5 ** 2 ** (0 ** 1)
==
5 ** (2 ** 0)
==
5 ** 1
==
5
Exponentiation is right-associative, so your expression is the same as
5 ** (2 ** (0 ** 1))
== 5 ** (2 ** 0)
== 5 ** 1
== 5
where any integer raised to the zeroth power is 1 by definition.
Others have already pointed this out already, but I just wanted to mention the documentation. You can see this by typing help("OPERATORS") in your repl. There you will spot somewhere at the top:
Operators in the same box group left to right (except for exponentiation, which groups from right to left).
You are right to be surprised though, this seems like a very odd decision to me. In other languages, e.g. octave, 5 ^ 2 ^ 0 ^ 1 == 1 as you'd expect. Oddly enough, both Julia and R agree with python on this.
EDIT: On second thought, I suppose making exponentiation right-associative makes sense too; you would expect
to mean 5^8 rather than 25^3 ...
Incidentally, here's another trap. How much is: 5 * -1 ** 2 ? Is it 5 or -5?
See help("**") to see why it is what it is. (incidentally, this is how octave, R, and julia treat this case too).
The moral of the story is: always group when there is potential for ambiguity. Or at least check the precedence is what you think it is before ungrouping.
** is exponentiation.
0 raised to the power of 1 is 0. So, we could re-write the statement as print(5**2**0) without changing the result.
2 raised to the power of 0 is 1. So, we can re-write the statement as print(5**1) without changing the result.
5 raised to the power of 1 is 5. So, we can rewrite the statement as print(5) without changing the result.

Python Math Giving Incorrect Output [duplicate]

This question already has answers here:
Python and Powers Math
(3 answers)
Closed last year.
Python is being weird again. When I put 5 * (40 ^ 2) + 50 * 40 + 100 it returns 2310. But on a calculator its 10100. I don't know why Python is making this mistake nor how to fix it. Anyone got any ideas?
If you will write
print( 5 * 40 ** 2 + 50 * 40 + 100 )
you will get the expected result.
10100
Try this instead:
5 * (40 ** 2) + 50 * 40 + 100

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__.

Issues with Recursion using Python 3

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.

Categories

Resources