Changing an iterative function into a recursive one - python

Consider:
def itr(n):
s = 0
for i in range(0, n+1):
s = s + i * i
return s
This is a simple iterative function that I would like to change into a recursive function.
def rec(n):
import math
if n!=0:
s=n-(2*math.sqrt(n))
if s!=0:
return(s+rec(n))
else:
return(n)
else:
return n
This is my try at doing the said thing, but I cannot quite get it right.
Why does not my solution work? What is the solution?

Use:
def recursive(total, n):
if n == 0:
return total
else:
return recursive(total + n * n, n - 1)
A couple of thoughts:
This can be refactored to using only a single argument, but by supplying both the total and the current iteration count, it is easier to see how to transform the iterative approach to a recursive one.
While this function can be made recursive, it should not be, as there isn't any advantage over the iterative approach.

Related

Sometimes I want a generator

The following code took me by surprise. I was hoping that I could write a function that might or might not act as a generator.
def mayGen(limit=5, asGenerator=False):
result = []
for i in range(1, limit + 1):
n = i * i
if asGenerator:
yield n
else:
result.append(n)
return result
print(mayGen(5, False))
for x in mayGen(5, True):
print(x)
But no, the output of this program is
<generator object mayGen at 0x7fa57b6ea7b0>
1
4
9
16
25
Calling mayGen with asGenerator=False is simply useless. It seems that the mere existence of a yield statement, regardless of whether it is executed, radically changes the behavior of the containing function.
So what if mayGen was actually big and complicated and I wish I could ask it to either function as a generator, or write its computed output to a file? Is there a well-traveled path here? How are people handling this sort of thing?
Just write a generator. You can always take its output and do whatever you want with it: put it in a list, write it to a file, etc. Although, if that's what you're going to be using it for a lot, you might want to write a wrapper function to do it for you, for example:
def willGen(limit=5):
for i in range(1, limit+1):
n = i * i
yield n
def willReturnList(*args, **kwargs):
return list(willGen(*args, **kwargs))
Edit: For a number of reasons mentioned in comments, this is probably not a great idea. In any event, you can write this:
def mayGen(limit=5, asGenerator=False):
def _generate(limit):
for i in range(1, limit + 1):
n = i * i
yield n
def _return(limit):
result = []
for i in range(1, limit + 1):
n = i * i
result.append(n)
return result
if asGenerator:
return _generate(limit)
return _return(limit)
Edit:
to simiplify even further, you should probably just return list(_generate(limit)) in the final line.

Nth Fibonacci in python

def fibonaci(i,memo):
if i == 0 or i == 1:
return i
if memo[i]==-1:
memo[i] = fibonaci(i-1,memo) + fibonaci(i-2,memo)
return memo[i]
def fibo(n):
a = []
return fibonaci(n,a)
print(fibo(2))
I'm a Java programmer learning python. This algorithm computes the nth fibonacci number using recursion + memoization. I don't understand why I'm seeing this error "IndexError: list index out of range" when running the program in python. Can anybody help? Thanks a ton!
As suggested in the comments to your question, there is no way memo[i]==-1 could be true.
I understand your want to test something like "if the value for fibonacci(i) has not yet been memoized", but the way Python will tell you in index is not present is certainly not by returning some magic value (like -1), but instead by raising an exception.
Looking up "EAFP" (easier to ask for forgiveness than permission) on your favorite search engine might show you why exceptions are not to be understood as errors, in python.
In addition, memoization will be preferably implemented by a dictionary, rather than a list (because dictionaries allow to map a value to any possible key, not necessarily to the next integer index value).
Without changing too much to the structure of your program, I would suggest the following :
def fibonaci(i,memo):
if i == 0 or i == 1:
return i
try:
memo[i]
except KeyError:
memo[i] = fibonaci(i-1,memo) + fibonaci(i-2,memo)
return memo[i]
def fibo(n):
a = {}
return fibonaci(n,a)
print(fibo(2))
I made few changes in your code to get nth Fibonacci no.(there might be other way too)
def fibonaci(i,memo):
if i == 0 or i == 1:
return i
if memo[i] == -1:
memo[i] = fibonaci(i-1,memo) + fibonaci(i-2,memo)
return memo[i]
def fibo(n):
a = [-1] * n
return fibonaci(n-1,a)
print(fibo(5))
print(fibo(10))
print(fibo(13))
print(fibo(57))
And output is :-
3
34
144
225851433717
You are seeing the error because you create an empty list with a = [] and then you try to look into it. You need to create a list long enough to index from zero up to n-1.
That being said, an easy way to do memoisation in Python is using the cache function from functools. This code does the memoisation for you:
from functools import cache
#cache
def fib(n):
if n <= 1:
return n
else:
return fib(n - 1) + fib(n - 2)
for n in range(10):
print(f"{fib(n) = }")
It isn't quite as efficient in running time, but in programmer time it is.

Are both of these proper use of recursion? Is one better than the other?

I am trying to better understand recursion.
As an example, here are two definitions for calculating n! :
def factorial(num, p=1):
if num == 0:
return p
else:
p *= num
return factorial(num-1,p)
and
def factorial(num):
if num == 1:
return num
return num * factorial(num - 1)
Is one better than the other? I understand that the second one builds a call stack, while the first is calculating p at each step and just passing it to another iteration of the function. Are there separate names for these types of functions? Is the first one not recursion?
Just looking for some guidance on understanding the differences here. Thanks!
There is little difference between the two. I would say "simpler is better" and the 2nd one involves fewer variables so I would go for that one.
Or something even simpler:
def factorial(N): return max(1,N) if N<3 else N*factorial(N-1)
In real life though, you shouldn't implement it recursively because iterative solutions will be faster.

Make fibo faster [duplicate]

This question already has answers here:
Efficient calculation of Fibonacci series
(33 answers)
Closed 4 years ago.
I need to write a code to give a number and print me the F[number].This code is pretty slow.Any ideas for a faster code?
while True:
n=input()
if n=='END' or n=='end':
break
class Fibonacci:
def fibo(self, n):
if int(n) == 0:
return 0
elif int(n) == 1:
return 1
else:
return self.fibo(int(n)-1) + self.fibo(int(n)-2)
f=Fibonacci()
print(f.fibo(n))
I have written a bit about faster fibonacci in this post, maybe one of them is useful for you? https://sloperium.github.io/calculating-the-last-digits-of-large-fibonacci-numbers.html
Anyway. Your code is very slow because you get exponential running time calling the same subtrees over and over.
You can try a linear solution:
def fib3(n):
if n == 0:
return 0
f1 = 0
f2 = 1
for i in range(n-1):
f1,f2 = f2, f1+f2
return f2
You can use functools memoize to make it store previous values so it doesn't have to recursively call the fibonacci function. The example they list is literally fibonacci
You can use a dict to memoize the function:
class Fibonacci:
memo = {}
def fibo(self, n):
if n in self.memo:
return self.memo[n]
if int(n) == 0:
value = 0
elif int(n) == 1:
value = 1
else:
value = self.fibo(int(n) - 1) + self.fibo(int(n) - 2)
self.memo[n] = value
return value
Use dynamic programming: this prevents it calculating all the way down to 0 and 1 each time:
memory = {0:0, 1:1}
def fibo(n):
if n in memory:
return memory[n]
else:
ans = fibo(int(n)-1) + fibo(int(n)-2)
memory[n] = ans
return ans
Test:
>>> fibo(1000)
43466557686937456435688527675040625802564660517371780402481729089536555417949051890403879840079255169295922593080322634775209689623239873322471161642996440906533187938298969649928516003704476137795166849228875
This is almost instantaneous.
Don't use a class; you're not gaining anything from it
Don't needlessly redefine your class each loop
Convert from str to int once, up front, rather than over and over
(If not required by assignment) Use iterative solution, not recursive
With just #1-3, you'd end up with:
def fibo(n): # Using plain function, defined once outside loop
if n < 2:
return n
return fib(n - 1) + fibo(n - 2)
while True:
n = input()
if n.lower() == 'end':
break
print(fibo(int(n))) # Convert to int only once
If you're not required to use a recursive solution, don't; Fibonacci generation is actually a pretty terrible use for recursion, so redefine the function to:
def fibo(n):
a, b = 0, 1
for i in range(n):
a, b = b, a + b
return a
which performs O(n) work rather than O(2**n) work. Memoizing could speed up the recursive solution (by decorating fibo with #functools.lru_cache(maxsize=None)), but it's hardly worth the trouble when the iterative solution is just so much faster and requires no cache in the first place.

recursive function to sum of first odd numbers python

I'm trying to convert below code to recursive function but seems i'm quite confusing how could i write below in recursive function. could help me to give some thoughts?
Basically, what I'm generating below is the sum of the first n odd numbers.
def sum_odd_n(n):
total=0
j=2*n-1
i=1
if i>j:
return 1
else:
total =((j+1)/2)**2
i+=2
return total
> >>> sum_odd_n(5)
> 25.0
> >>> sum_odd_n(4)
> 16.0
> >>> sum_odd_n(1)
> 1.0
This smells somewhat like homework so I'm going to offer some advice instead of a solution.
Recursion is about expressing a problem in terms of itself.
Suppose you know the sum of the odd numbers from N to N - 2.
Can you write the total sum in terms of this sum and the function itself (or a related helper function)?
Recursive functions have at least one base case and at least one recursive call. Here are some hints:
def f(n):
# Base case - for which
# n do we already know the answer
# and can return it without
# more function calls? (Clearly,
# this must also terminate any
# recursive sequence.)
if n == ???:
return ???
# Otherwise, lets say we know the answer
# to f(n - 1) and assign it to
# the variable, 'rest'
rest = f(n - 1)
# What do we need to do with 'rest'
# to return the complete result
return rest + ???
Fill out the question marks and you'll have the answer.
Try:
def sum_of_odd(n):
if n>0:
x=(n*2)-1
return x+sum_of_odd(n-1)
else:
return 0
The answer of this:
sum_of_odd(5)
will be:
25
Try :
def sum_odd_n(n):
if n>0:
if n==1:
return 1
else:
return 2*n-1 + sum_odd_n(n-1)

Categories

Resources