Nth Fibonacci in python - 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.

Related

how does python indexing affect runtime of o-notation?

I am new to O-notation and am trying to find the worst-case runtime for some of my codes. The only issue is that I'm confused on how O-notation runs with indexing and appending so I thought I'd ask for help with the following sample codes:
def sums_1(L):
n = len(L)
tot = 0
M = []
for i in L[:n//2]:
M.append(i)
for i in L[n//2:]:
M.extend(L)
return sum(M)
def sums_2(s):
def help_e(s, pos):
if pos >= len(s):
return ''
return help_e(s, pos+1) + s[pos]
return help_e(s, 0)
I think both codes would run o(n) times but I wanted some clarification on indexing and how that may affect the runtime, thanks!
Here you have the wiki file for the big-o notation for almost every python data-structure operations: https://wiki.python.org/moin/TimeComplexity

python build a list recursively

Let's say I have a string
S = "qwertyu"
And I want to build a list using recursion so the list looks like
L = [u, y, t, r, e, w, q]
I tried to write code like this:
def rec (S):
if len(S) > 0:
return [S[-1]].append(rec(S[0:-1]))
Ideally I want to append the last element of a shrinking string until it reaches 0
but all I got as an output is None
I know I'm not doing it right, and I have absolutely no idea what to return when the length of S reaches 0, please show me how I can make this work
(sorry the answer has to use recursion, otherwise it won't bother me)
Thank you very much!!!
There are many simpler ways than using recursion, but here's one recursive way to do it:
def rec (S):
if not S:
return []
else:
temp = list(S[-1])
temp.extend(rec(S[:-1]))
return temp
EDIT:
Notice that the base case ensures that function also works with an empty string. I had to use temp, because you cannot return list(S[-1]).extend(rec(S[:-1])) due to it being a NoneType (it's a method call rather than an object). For the same reason you cannot assign to a variable (hence the two separate lines with temp). A workaround would be to use + to concatenate the two lists, like suggested in Aryerez's answer (however, I'd suggest against his advice to try to impress people with convoluted one liners):
def rec (S):
if not S:
return []
else:
return list(S[-1]) + rec(S[:-1])
In fact using + could be more efficient (although the improvement would most likely be negligible), see answers to this SO question for more details.
This is the simplest solution:
def rec(S):
if len(S) == 1:
return S
return S[-1] + rec(S[:-1])
Or in one-line, if you really want to impress someone :)
def rec(S):
return S if len(S) == 1 else S[-1] + rec(S[:-1])
Since append mutates the list, this is a bit difficult to express recursively. One way you could do this is by using a separate inner function that passes on the current L to the next recursive call.
def rec(S):
def go(S, L):
if len(S) > 0:
L.append(S[-1])
return go(S[0:-1], L)
else:
return L
return go(S, [])
L = [i for i in S[::-1]]
It should work.

How to decrease running time of this particular solution?

I am trying to write a piece of code that will generate a permutation, or some series of characters that are all different in a recursive fashion.
def getSteps(length, res=[]):
if length == 1:
if res == []:
res.append("l")
res.append("r")
return res
else:
for i in range(0,len(res)):
res.append(res[i] + "l")
res.append(res[i] + "r")
print(res)
return res
else:
if res == []:
res.append("l")
res.append("r")
return getSteps(length-1,res)
else:
for i in range(0,len(res)):
res.append(res[i] + "l")
res.append(res[i] + "r")
print(res)
return getSteps(length-1,res)
def sanitize(length, res):
return [i for i in res if len(str(i)) == length]
print(sanitize(2,getSteps(2)))
So this would return
"LL", "LR", "RR, "RL" or some permutation of the series.
I can see right off the bat that this function probably runs quite slowly, seeing as I have to loop through an entire array. I tried to make the process as efficient as I could, but this is as far as I can get. I know that some unnecessary things happen during the run, but I don't know how to make it much better. So my question is this: what would I do to increase the efficiency and decrease the running time of this code?
edit = I want to be able to port this code to java or some other language in order to understand the concept of recursion rather than use external libraries and have my problem solved without understanding it.
Your design is broken. If you call getSteps again, res won't be an empty list, it will have garbage left over from the last call in it.
I think you want to generate permutations recursively, but I don't understand where you are going with the getSteps function
Here is a simple recursive function
def fn(x):
if x==1:
return 'LR'
return [j+i for i in fn(x-1) for j in "LR"]
Is there a way to combine the binary approach and a recursive approach?
Yes, and #gribbler came very close to that in the post to which that comment was attached. He just put the pieces together in "the other order".
How can you construct all the bitstrings of length n, in increasing order (when viewed as binary integers)? Well, if you already have all the bitstrings of length n-1, you can prefix them all with 0, and then prefix them all again with 1. It's that easy.
def f(n):
if n == 0:
return [""]
return [a + b for a in "RL" for b in f(n-1)]
print(f(3))
prints
['RRR', 'RRL', 'RLR', 'RLL', 'LRR', 'LRL', 'LLR', 'LLL']
Replace R with 0, and L with 1, and you have the 8 binary integers from 0 through 7 in increasing order.
You should look into itertools. There is a function there called permutations which does exactly what you want to achieve here.

Python return statement not running

I am creating a program to figure out the highest number of decimals in a list of numbers. Basically, a list with [123, 1233] would return 4 because 1233 has four numbers in it and it is the largest. Another example would be that [12, 4333, 5, 555555] would return 6 because 555555 has 6 numbers.
Here is my code.
def place(listy):
if len(listy) == 1:
decimal = len(str(listy[0]))
print(decimal)
else:
if len(str(listy[0])) >= len(str(listy[1])):
new_list = listy[0:1]
for i in listy[2:]:
new_list.append(i)
place(new_list)
else:
place(listy[1:])
Now, when I use print(decimal) it works, but if I change print(decimal) to return decimal, it doesn't return anything. Why is this? How do I fix this? I have come across these return statements which doing run a lot of times. Thanks in advance!
When you do a recursive call (i.e. when place calls place, and the called place returns a value, then the calling place must return it as well (i.e. the return value "bubbles up" to the initial caller).
So you need to replace every recursive call
place(...)
with
return place(...)
As others have said, there are easier solutions, such as using max(). If you want to keep a recursive approach, I would refactor your code as follows:
def place2(listy):
if len(listy) < 1:
return None
elif len(listy) == 1:
return len(str(listy[0]))
else:
v0, v1 = listy[0], listy[1]
if v1 > v0:
return place2(listy[1:])
else:
return place2([listy[0]]+listy[2:])
Although this is tail-recursive, Python does not really care so this approach will be inefficient. Using max(), or using a loop will be the better solution in Python.
It's not that the return doesn't do anything, it's that you don't propagate the return from your recursive call. You need a few more returns:
def place(listy):
if len(listy) == 1:
decimal = len(str(listy[0]))
return decimal
else:
if len(str(listy[0])) >= len(str(listy[1])):
new_list = listy[0:1]
for i in listy[2:]:
new_list.append(i)
return place(new_list) # <-- return added
else:
return place(listy[1:]) # <-- return added
You can see the print at any level, but to get it back to the caller it needs to be propagated.
The function does return the value, but it's not printing it out.
A simple way to solve this is, just call the function within a print statement.
That is:
print(place(listy))
If all you want is to find the maximum length of a list of integers, consider:
max([len(str(n)) for n in N])
For example
N = [1,22,333,4444]
max([len(str(n)) for n in N]) # Returns 4
N = [12, 4333, 5, 555555]
max([len(str(n)) for n in N]) # Returns 6
Note: This will only work for positive integers.
Or more simply:
len(str(max(N)))
Which will also only work for positive integers.
Use ''global variable'' (google it) to access and change a variable defined outside of your function.

recursive method returning original value

So I'm trying to learn python on my own, and am doing coding puzzles. I came across one that pretty much ask for the best position to stand in line to win a contest. The person running the contest gets rid of people standing in odd number positions.
So for example if 1, 2, 3, 4, 5
It would get rid of the odd positions leaving 2, 4
Would get rid of the remaining odd positions leaving 4 as the winner.
When I'm debugging the code seems to be working, but it's returning [1,2,3,4,5] instead of the expected [4]
Here is my code:
def findWinner(contestants):
if (len(contestants) != 1):
remainingContestants = []
for i, contestant in enumerate(contestants, 1):
if (isEven(i)):
remainingContestants.append(contestant)
findWinner(remainingContestants)
return contestants
Am I not seeing a logic error or is there something else that I'm not seeing?
You must return the value from the recurse function to the caller function:
return findWinner(remainingContestants)
else you would return just the original value without any changes.
def findWinner(contestants):
if (len(contestants) != 1):
remainingContestants = []
for i, contestant in enumerate(contestants, 1):
if (isEven(i)):
remainingContestants.append(contestant)
return findWinner(remainingContestants) # here the value must be return
return contestants # without the return above, it will just return this value(original)
How about this:
def findWinner(contestants):
return [contestants[2**int(math.log(len(contestants),2))-1]]
I know its not what the questions really about but I had to =P. I cant just look at all that work for finding the greatest power of 2 less than contestants and not point it out.
or if you don't like the 'artificial' solution and would like to actually perform the process:
def findWinner2(c):
while len(c) > 1:
c = [obj for index, obj in enumerate(c, 1) if index % 2 == 0] #or c = c[1::2] thanks desfido
return c
you shold use
return findWinner(remaingContestants)
otherwise, of course, your list will never be updated and so your func is gonna always return containts
however, see the PEP8 for style guide on python code: http://www.python.org/dev/peps/pep-0008/
the func isEven is probably an overkill...just write
if not num % 2
finally, recursion in python isn't recommended; make something like
def find_winner(alist):
while len(alist) > 1:
to_get_rid = []
for pos, obj in enumerate(alist, 1):
if pos % 2:
to_get_rid.append(obj)
alist = [x for x in alist if not (x in to_get_rid)]
return alist
Is there a reason you're iterating over the list instead of using a slice? Doesn't seem very python-y to not use them to me.
Additionally, you might want to do something sensible in the case of an empty list. You'll currently go into an infinite loop.
I'd write your function as
def findWinner(contestants):
if not contestants:
raise Exception
if len(contestants)==1:
return contestants[0]
return findWinner(contestants[1::2])
(much as #jon_darkstar's point, this is a bit tangential to the question you are explicitly asking, but still a good practice to engage in over what you're doing)
You are missing a return at the line where you call "findWinner"

Categories

Resources