I wrote this python code to give the Harmonic Series of a certain value n both recursively and iteratively. Here is the functions:
def H(n):
if (n <= 0): raise ValueError("n must be bigger than 0.")
if (n == 1): return 1
else: return sum([1/x for x in range(1,n+1)])
def H_rec(n):
if (n <= 0): raise ValueError("n must be bigger than 0.")
if (n == 1): return 1
else: return 1/n + H(n-1)
Then, when I run the code 10 times for each, I get the following times:
RECURSIVE TIMES [22.755768060684204, 17.231882095336914, 14.965636014938354, 14.277509927749634, 14.0553719997406, 13.788002014160156, 13.338942766189575, 13.72638201713562, 14.690818071365356, 14.236813068389893]
RECURSIVE MEAN: 15.30671260356903
ITERATIVE TIMES [15.093524932861328, 12.801156759262085, 13.350629091262817, 13.806081056594849, 13.29387378692627, 13.876484870910645, 12.934008121490479, 13.859009981155396, 13.350301027297974, 13.590226888656616]
ITERATIVE MEAN: 13.595529651641845
The code is supposed to find H_rec(100000000) and H(100000000), which are fairly big numbers.
I don't understand exactly why the recursive definition takes longer when the way it's defined seems to require less computation. The iterative one requires forming a list and finding its sum, while the recursive one just sums 1/n + H(n-1).
What is so misleading about this recursion? Why is it slow?
Your recursive function is calling the iterative one in else: return 1/n + H(n-1), you need to modify it as the following:
def H_rec(n):
if (n <= 0): raise ValueError("n must be bigger than 0.")
if (n == 1): return 1
else: return 1/n + H_rec(n-1) #Fix this line
Code executed inside the Python interpreter is fastest. Python code (which is compiled to Python byte code that is interpreted by a virtual machine) is slower. User-defined function calls are slowest of all, because the virtual machine has to manage its own call stack to track the execution environments.
Consider the following code:
def S1(n):
return sum(range(1,n))
def S2(n):
rv = 0
for i in range(1,n):
rv += i
return rv
def S3(n):
if n == 0:
return 0
else:
return n + S3(n-1)
S1 is the fastest; as much work as possible is pushed into the interpreter. S2 is slower because now each addition is a separate Python instruction to be interpreted. S3 is slowest because now each addition involves another function call to get its second operand; as noted before, function calls are slow in Python.
>>> print(timeit.timeit('S1(50)', globals=globals()))
1.2118524569996225
>>> print(timeit.timeit('S2(50)', globals=globals()))
3.262354401002085
>>> print(timeit.timeit('S3(50)', globals=globals()))
10.102635376999388
Related
I've made two functions for computing the Fibonacci Sequence, one using recursion with memory and one using a loop;
def fib_rec(n, dic = {0 : 0, 1 : 1}):
if n in dic:
return dic[n]
else:
fib = fib_rec(n - 2, dic) + fib_rec(n - 1, dic)
dic[n] = fib
return fib
def fib_loop(n):
if n == 0 or n == 1:
return n
else:
smaller = 0
larger = 1
for i in range(1, n):
smaller, larger = larger, smaller + larger
return larger
I've heard that the Fibonacci Sequence often is solved using recursion, but I'm wondering why. Both my algorithms are of linear time complexity, but the one using a loop will not have to carry a dictionary of all past Fibonacci numbers, it also won't exceed Python's recursion depth.
Is this problem solved using recursion only to teach recursion or am I missing something?
The usual recursive O(N) Fibonacci implementation is more like this:
def fib(n, a=0, b=1):
if n == 0: return a
if n == 1: return b
return fib(n - 1, b, a + b)
The advantage with this approach (aside from the fact that it uses O(1) memory) is that it is tail-recursive: some compilers and/or runtimes can take advantage of that to secretly convert it to a simple JUMP instruction. This is called tail-call optimization.
Python, sadly, doesn't use this strategy, so it will use extra memory for the call stack, which as you noted quickly runs into Python's recursion depth limit.
The Fibonacci sequence is mostly a toy problem, used for teaching people how to write algorithms and about big Oh notation. It has elegant functional solutions as well as showing the strengths of dynamic programming (basically your dictionary-based solution), but it's also practically a solved problem.
We can also go a lot faster. The page https://www.nayuki.io/page/fast-fibonacci-algorithms describes how. It includes a fast doubling algorithm written in Python:
#
# Fast doubling Fibonacci algorithm (Python)
# by Project Nayuki, 2015. Public domain.
# https://www.nayuki.io/page/fast-fibonacci-algorithms
#
# (Public) Returns F(n).
def fibonacci(n):
if n < 0:
raise ValueError("Negative arguments not implemented")
return _fib(n)[0]
# (Private) Returns the tuple (F(n), F(n+1)).
def _fib(n):
if n == 0:
return (0, 1)
else:
a, b = _fib(n // 2)
c = a * (b * 2 - a)
d = a * a + b * b
if n % 2 == 0:
return (c, d)
else:
return (d, c + d)
def fac(n):
if (n < 1):
return 1
else:
n * fac(n-1)
print fac(4)
Why does return command cause the function to go back up and multiply the factorials? I have trouble understanding this.
1). You need to write a return in your code.
def fac(n):
if (n < 1):
return 1
else:
return n * fac(n-1)
print(fac(4))
2). I am uploading a picture which will help you to understand the concept of recursion. Follow the arrow from start to end in the picture.
You need to consider the call stack. The way this code needs to operate is for every fac(n) is equal to n * (n-1) * (n-2) ... (n - n + 1).
Your code was wrong. For a recursive function to work, you must be returning a value each time. You were getting None back because you never returned anything.
def fac(n):
if n == 1:
return 1
return n * fac(n - 1)
if __name__ == "__main__":
num = 2
print(fac(num))
Keep practicing recursion. It is extremely useful and powerful.
For more of a why it works than how it works perspective:
Calling a function recursively is no different than calling a different function: it just happens to execute the same set of instructions. You don't worry about Python getting confused between locals and parameters of different functions, or knowing where to return to; this is no different. In fact, if each recursive invocation had its own name instead of re-using the same name, you probably wouldn't be here.
Showing that it works properly is a bit different, because you need to make sure the recursion ends at some point. But this is no different than worrying about infinite loops.
I am a bit of a novice when it comes to Python (I just started learning it two weeks ago) but am having a lot of fun completing challenges. One challenge that is giving me trouble is a variation on the Fibonacci sequence with mortal rabbits (i.e. the Padovan sequence, but with variable lifespan).
After much trial and error, I have written a code that returns outputs that match with the tables I've made for different lifespan settings. However, the algorithm gets really slow at around 40 turns and for lifespans over 15 months, and the challenges are timed. The code I made is:
def fib(n):
if n == 1:
return 1
elif n == 2:
return 1
else:
return fib(n-1) + fib(n-2)
def fibd(n, m):
if n < (m+2) and m > 3:
return fib(n)
elif n < (m+1) and m == 3:
return fib(n)
elif n >= (m+1) and m==3:
return fibd(n-2, m) + fibd(n-3, m)
elif n >= (m+2) and m > 3:
return sum(map(fibd, [n-(m+x) for x in (range(-(m-2), 2))], [m]*m))
That's as simple as I could make it with my modest skillset, but I'm at a loss. n represents the number of turns where we stop counting, and m represents the number of turns each rabbit lives before dying. Basically, the first function gives the Fibonacci sequence. This is used because until the rabbits actually die, the Padovan generalization is identical to a Fibonacci sequence, so I need to call back to it as part of the recursion. Then, there is a separate chunk of code for a lifespan of 3, because while for lifespans of 4 or more, the formula for obtaining the number of rabbits on the nth turn is (n-2)+(n-3)...(n-(m+1)), with a lifespan of 3, the formula is different (it's just (n-2)+(n-3). The next chunk of code is for lifespans of 4 or greater.
Any input would be appreciated, even some hints if you don't have a total solution. I am interested in learning how to optimize just as much as I am interested in solving this particular problem, since this isn't for work or school but rather for my own edification.
Both your functions have exponential time complexity, so they will be very slow. You will need to find a better algorithm. While you are thinking, you might want to run this program in Python, say fibs(50), and see what it does:
def fibs(n):
a,b = 1,1
while n > 0:
print a
a,b,n = b,a+b,n-1
Below is a rework of your code with a better recursive Fibonacci function (though not as fast as iterative) and memoizing, which #JohnY couldn't fit in his comment, from the blog Memoized recursive fibonacci in Python
The upgraded Fibonacci function alone improves the speed by about 20x. The memoize decorator, which caches values so they don't get recomputed, improves the speed by 40x. (My rewrite of fibd() makes no difference performance-wise, sigh.)
def memoize(f):
cache = {}
return lambda *args: cache[args] if args in cache else cache.update({args: f(*args)}) or cache[args]
#memoize
def fib(n, res=0, nxt=1):
if n == 0:
return res
return fib(n - 1, nxt, res + nxt)
#memoize
def fibd(n, m):
if m > 3:
if n < (m + 2):
return fib(n)
return sum(map(fibd, (n - (m + x) for x in range(2 - m, 2)), [m] * m))
elif m == 3:
if n < (m + 1):
return fib(n)
return fibd(n - 2, m) + fibd(n - 3, m)
print(fibd(40, 15))
Overall performance went from 47 seconds to 1/20th of a second on my system. However, I only tested the fibd(40, 15) case, you'll need to do more testing of this modified code.
I have a line code like this -
while someMethod(n) < length and List[someMethod(n)] == 0:
# do something
n += 1
where someMethod(arg) does some computation on the number n. The problem with this code is that I'm doing the same computation twice, which is something I need to avoid.
One option is to do this -
x = someMethod(n)
while x < length and List[x] == 0:
# do something
x = someMethod(n + 1)
I am storing the value of someMethod(n) in a variable x and then using it later. However, the problem with this approach is that the code is inside a recursive method which is called multiple times. As a result, a lot of excess instances of variables x are being created which slows the code down.
Here's the snipped of the code -
def recursion(x, n, i):
while someMethod(n) < length and List[someMethod(n)] == 0:
# do something
n += 1
# some condition
recursion(x - 1, n, someList(i + 1))
and this recursion method is called many times throughout the code and the recursion is quite deep.
Is there some alternative available to deal with a problem like this?
Please try to be language independent if possible.
You can use memoization with decorators technique:
def memoize(f):
memo = dict()
def wrapper(x):
if x not in memo:
memo[x] = f(x)
return memo[x]
return wrapper
#memoize
def someMethod(x):
return <your computations with x>
As i understand your code correctly you are looking for some sort of memorization.
https://en.wikipedia.org/wiki/Memoization
it means that on every recursive call you have to save as mush as possible past calculations to use it in current calculation.
I'm solving Euler problem set #2, and I have found the function relevant to what I want I want to do, well partially at least since I'll need to make modifications for even numbers in the fibonacci sequence. I'm trying to print out fibonacci numbers, up to a certain point, n.
def fib(n):
if n == 1:
return 1
elif n == 0:
return 0
else:
return fib(n-1) + fib(n-2)
Giving
>>> fib(13)
233
However
>>> fib(200)
returns nothing. I'm not sure if it is taking long to compute, or whatnot. I might try this in C++, but would like some knowledge from here first.
It's just taking a long time to compute, because you're recursively calling fib() many, many times. If you add a line as follows you can get status update on it as it runs. It'll take a very long time because of the amount of recursion (if it even finishes).
def fib(n):
print("Fibonacci: {0}".format(n))
if n == 1:
return 1
elif n == 0:
return 0
else:
return fib(n-1) + fib(n-2)