Is nested recursion possible or should we avoid recursion ? - python

I came across a question like this
F(1) = 1
F(2n) = F(n)
F(2n+1) = F(n) + F(n+1)
Develop a recursive program to compute F
Some user mentioned using two recursive function calls:
def calc(n):
if n=1 :
return 1
else if(n%2)==0:
return calc(n/2)
else :
return calc(n/2)+calc(n/2+1) **NESTED RECURSION**
Is this a correct logic? Wouldn't the algorithm be exponentially large?
I thought of a simple code like :
def calc(count):
result[1]=1
n=2
for range(1,count):
if n%2=0:
result.append(result[n])
else :
result.append(result[n/2]+result[n/2+1])
return result

Both of these approaches are correct. It is indeed legal to have multiple recursive calls from a function, and the meaning is what you'd think - just do one call, then the next, then the next, etc.
Interestingly, I don't think that the recursive version does make exponentially many calls. It makes at most two recursive calls, but each are on a problem whose size is (approximately) half as large as the original call. Essentially, the recurrence looks like this:
T(1) = 1
T(2) = 1
T(n) <= T(n / 2) + T(n / 2 + 1) + 1
I use "less than or equal to here" to say that in the best case you might just make one call, but in the worst case you make at most two.
I want to prove that this function T(n) <= max{cn + d, a} for some constants c, d, and a. This would prove that T(n) = O(n) and thus makes at most linearly many calls. As our base case, we have that
T(1) = 1
T(2) = 1
so let's set a = 1. For the inductive step, we consider three cases. First, let's consider when floor(n / 2) <= 2 and floor(n / 2 + 1) <= 2:
T(n) <= T(n / 2) + T(n / 2 + 1) + 1
<= 1 + 1 + 1
<= 3
If we assume that cn + d >= 3, when n = 3 or n = 4, then this works out correctly. In particular, this means that 3c + d >= 3 and 4c + d >= 3.
In the next case, let's see what happens if floor(n / 2) <= 2 and floor(n / 2 + 1) >= 2. Then we have that
T(n) <= T(n / 2) + T(n / 2 + 1) + 1
<= 1 + max{c(n / 2 + 1) + d, 1} + 1
<= 2 + max{c(n / 2 + 1) + d, 1}
<= 3 + c(n / 2 + 1) + d
So if we have that 3 + c(n / 2 + 1) + d <= cn + d, this claim still holds. Note that we're only in this case if n = 5, and so this means that we must have that
3 + c(n / 2 + 1) + d <= cn + d
3 + c(n / 2 + 1) <= cn
3 + c(5 / 2 + 1) <= 5c
3 + 5c/2 + c <= 5c
3 + 7c/2 <= 5c
4 <= 3c / 2
8 / 3 <= c
So we must have that c >= 8 / 3.
And finally, the case where neither n / 2 nor n / 2 + 1 are less than three:
T(n) <= T(n / 2) + T(n / 2 + 1) + 1
<= c(n / 2) + d + c(n / 2 + 1) + d + 1
<= cn / 2 + cn / 2 + c + 2d + 1
= cn + c + 2d + 1
This is less than cn + d if
cn + c + 2d + 1 <= cn + d
c + 2d + 1 <= d
c + d + 1 <= 0
This works if d = -c - 1.
From earlier, we know that 3c + d >= 3, which works if 2c - 1 >= 3, or 2c >= 4, so c >= 2. We also have that 4c + d >= 3, which also works if c >= 2. Letting c = 8 / 3, we get that d = -11 / 3, so
T(n) <= max{8n/3 - 11/3, 1}
So T(n) = O(n) and the recursion makes only linearly many calls.
The short version of this is that both the recursive and iterative versions take linear time. Don't fear recursion's exponential-time blowup without being sure it's exponential. :-) Though admittedtly, in this case, I really like the iterative version more. It's clearer, more intuitive, and more immediately O(n).

Recursion isn't bad. Think of recursion as a tool. Some problems can be solved quite nicely with recursion (e.g. Fibonacci numbers), while other problems don't have a recursive bend (a worklist type algorithm).
Why people go awry with recursion is that they think a recursive solution will give them a good reputation. I'd say, solve the problem with the simplest approach possible.
Sometimes that approach is recursion, sometimes it isn't.
Also, a recursive approach can be harder to understand. So, you need to take readability into consideration.

It is easier to estimate the performance of your second algorithm: it is clearly O(n) in space and time, with a small constant. But that does not mean the second algorithm is faster than the first. In fact it isn't!
In Python 2.6.5, your second algorithm is 25x slower than the first at calculating calc(1000), 80x slower at calculating calc(10000), and 1712x slower at calculating calc(65535).
The worst-case behavior for the first algorithm, it seems, is for numbers like 54613 = 11010101010101012. For that value, the first algorithm is only 16x faster than the second.
In the simple recursive algorithm, calc(1000) only involves 50 calls to calc, and even calc(54613) requires only 5166.
This O(log n) algorithm is faster still:
def C(x):
a = 1
b = 0
# Loop invariant: we are going to return a*C(x) + b*C(x + 1).
while x >= 2:
if x % 2 == 0:
a += b
else:
b += a
x //= 2
return a + b
The performance of this algorithm is easy to evaluate, but its correctness isn't. ;-)

You can write a recursive version which is sublinear (O(log n)) in fact.
The key is to notice that you can compute two values for n and n+1 given the two values for [n/2] and [n/2]+1.
So if you think of the two values as one tuple T(n) = (F(n), F(n+1)), then given T([n/2]), you can compute T(n).
So your function will be something like
Tuple F(int n) {
// Base cases:
Tuple result;
if (n is even) {
Tuple t = F(n/2);
result.First = t.First;
result.Second =t.First + t.Second;
return result;
}
if (n is odd) {
Tuple t = F((n-1)/2);
result.First = t.First + t.Second;
result.Second = t.Second;
return result;
}
}
This way you end up making just one recursive call and since you halve the size of input for each call, it is recursive and O(logn).
Exercise: Give an O(log n) time algorithm for Fibonacci numbers using this trick.
Hint: Use the identities:

Related

Is ther any other way to get sum 1 to 100 with recursion?

I'm studing recursive function and i faced question of
"Print sum of 1 to n with no 'for' or 'while' "
ex ) n = 10
answer =
55
n = 100
answer = 5050
so i coded
import sys
sys.setrecursionlimit(1000000)
sum = 0
def count(n):
global sum
sum += n
if n!=0:
count(n-1)
count(n = int(input()))
print(sum)
I know it's not good way to get right answer, but there was a solution
n=int(input())
def f(x) :
if x==1 :
return 1
else :
return ((x+1)//2)*((x+1)//2)+f(x//2)*2
print(f(n))
and it works super well , but i really don't know how can human think that logic and i have no idea how it works.
Can you guys explain how does it works?
Even if i'm looking that formula but i don't know why he(or she) used like that
And i wonder there is another solution too (I think it's reall important to me)
I'm really noob of python and code so i need you guys help, thank you for watching this
Here is a recursive solution.
def rsum(n):
if n == 1: # BASE CASE
return 1
else: # RECURSIVE CASE
return n + rsum(n-1)
You can also use range and sum to do so.
n = 100
sum_1_to_n = sum(range(n+1))
you can try this:
def f(n):
if n == 1:
return 1
return n + f(n - 1)
print(f(10))
this function basically goes from n to 1 and each time it adds the current n, in the end, it returns the sum of n + n - 1 + ... + 1
In order to get at a recursive solution, you have to (re)define your problems in terms of finding the answer based on the result of a smaller version of the same problem.
In this case you can think of the result sumUpTo(n) as adding n to the result of sumUpTo(n-1). In other words: sumUpTo(n) = n + sumUpTo(n-1).
This only leaves the problem of finding a value of n for which you know the answer without relying on your sumUpTo function. For example sumUpTo(0) = 0. That is called your base condition.
Translating this to Python code, you get:
def sumUpTo(n): return 0 if n==0 else n + sumUpTo(n-1)
Recursive solutions are often very elegant but require a different way of approaching problems. All recursive solutions can be converted to non-recursive (aka iterative) and are generally slower than their iterative counterpart.
The second solution is based on the formula ∑1..n = n*(n+1)/2. To understand this formula, take a number (let's say 7) and pair up the sequence up to that number in increasing order with the same sequence in decreasing order, then add up each pair:
1 2 3 4 5 6 7 = 28
7 6 5 4 3 2 1  = 28
-- -- -- -- -- -- -- --
8 8 8 8 8 8 8 = 56
Every pair will add up to n+1 (8 in this case) and you have n (7) of those pairs. If you add them all up you get n*(n+1) = 56 which correspond to adding the sequence twice. So the sum of the sequence is half of that total n*(n+1)/2 = 28.
The recursion in the second solution reduces the number of iterations but is a bit artificial as it serves only to compensate for the error introduced by propagating the integer division by 2 to each term instead of doing it on the result of n*(n+1). Obviously n//2 * (n+1)//2 isn't the same as n*(n+1)//2 since one of the terms will lose its remainder before the multiplication takes place. But given that the formula to obtain the result mathematically is part of the solution doing more than 1 iteration is pointless.
There are 2 ways to find the answer
1. Recursion
def sum(n):
if n == 1:
return 1
if n <= 0:
return 0
else:
return n + sum(n-1)
print(sum(100))
This is a simple recursion code snippet when you try to apply the recurrent function
F_n = n + F_(n-1) to find the answer
2. Formula
Let S = 1 + 2 + 3 + ... + n
Then let's do something like this
S = 1 + 2 + 3 + ... + n
S = n + (n - 1) + (n - 2) + ... + 1
Let's combine them and we get
2S = (n + 1) + (n + 1) + ... + (n + 1) - n times
From that you get
S = ((n + 1) * n) / 2
So for n = 100, you get
S = 101 * 100 / 2 = 5050
So in python, you will get something like
sum = lambda n: ( (n + 1) * n) / 2
print(sum(100))

Time and Space complexity of Palindrome Partitioning II

So I was solving this LeetCode question - https://leetcode.com/problems/palindrome-partitioning-ii/ and have come up with the following most naive brute force recursive solution. Now, I know how to memoize this solution and work my way up to best possible with Dynamic Programming. But in order to find the time/space complexities of further solutions, I want to see how much worse this solution was and I have looked up in multiple places but haven't been able to find a concrete T/S complexity answer.
def minCut(s: str) -> int:
def is_palindrome(start, end):
while start < end:
if not s[start] == s[end]:
return False
start += 1
end -= 1
return True
def dfs_helper(start, end):
if start >= end:
return 0
if is_palindrome(start, end):
return 0
curr_min = inf
# this is the meat of the solution and what is the time complexity of this
for x in range(start, end):
curr_min = min(curr_min, 1 + dfs_helper(start, x) + dfs_helper(x + 1, end))
return curr_min
return dfs_helper(0, len(s) - 1)
Let's take a look at a worst case scenario, i.e. the palindrome check will not allow us to have an early out.
For writing down the recurrence relation, let's say n = end - start, so that n is the length of the sequence to be processed. I'll assume the indexed array accesses are constant time.
is_palindrome will check for palindromity in O(end - start) = O(n) steps.
dfs_helper for a subsequence of length n, calls is_palindrome once and then has 2n recursive calls of lengths 0 through n - 1, each being called two times, plus the usual constant overhead that I will leave out for simplicity.
So, we have
T(0) = 1
T(n) = O(n) + 2 * (sum of T(x) for x=0 to n-1)
# and for simplicity, I will just use
T(n) = n + 2 * (sum of T(x) for x=0 to n-1)
This pattern already has to be at least exponential. We can look at the next few steps:
T(1) = 3 = 1 + 2 * 1 = 1 + 2 * (T(0))
T(2) = 10 = 2 + 2 * 4 = 2 + 2 * (T(0) + T(1))
T(3) = 31 = 3 + 2 * 14 = 3 + 2 * (T(0) + T(1) + T(2))
T(4) = 94 = 4 + 2 * 45 = 4 + 2 * (T(0) + T(1) + T(2) + T(3))
which looks as if this grows approximately as fast as 3^n. We can also show that for n > 2:
T(n) = n + 2 * (sum of T(x) for x=0 to n-1)
T(n) = n + 2 * (T(0) + T(1) + ... + T(n-1))
T(n) = n + 2 * (T(0) + T(1) + ... + T(n-2)) + 2 * T(n-1)
T(n) = 1 + n-1 + 2 * (T(0) + T(1) + ... + T(n-2)) + 2 * T(n-1)
# with
T(n-1) = n-1 + 2 * (sum of T(x) for x=0 to n-2)
T(n-1) = n-1 + 2 * (T(0) + T(1) + ... + T(n-2))
# we can substitute:
T(n) = 1 + T(n-1) + 2 * T(n-1)
T(n) = 1 + 3 * T(n-1)
So, if I'm not mistaken, the asymptotic time complexity should be in θ(3^n), or, allow me to make that joke, even worse than O(no).
For Space complexity: Your function does not explicitly allocate any memory. So, there is only the constant overhead for recursing (assuming python does not optimize this out). The important aspect here is that the two recursion steps will happen one after the other, so that we get the recurrence:
S(0) = 1
S(n) = 1 + S(n-1)
which gives us a space complexity in θ(n).

How to optimize a algorithm that uses loops to find a stable value for a variable

I have a case where a variable (a, in this case) is calculated at each loop iteration and stops where the increment of value between two iterations is small enough.
I would like to know of a general way to find the value for that variable in this kind of case, without having to do that "convergence" work using loops.
There I would like to know if the solution is to put everything in equations, or if some tools exist to tackle that.
a = 10
b = 10
diff = 1
while diff > .1:
old_a = a
a += b
diff = 1 - (old_a/a)
print(diff)
The present code produces:
0.5
0.33333333333333337
0.25
0.19999999999999996
0.16666666666666663
0.1428571428571429
0.125
0.11111111111111116
0.09999999999999998
Therefore, it takes 9 iterations to find a relative difference of the value of a between two iterations inferior to 10%.
You have
a_n = a_0 + n * b
and try to find where
1 - (a_(n-1) / a_n)
= 1 - (a_0 + (n--1)*b) / ( a_0 + n * b)
= 1 - (a_0 + n*b -b) / (a_0 + n*b)
= 1 - 1 + b / (a_0 + n*b)
= b / (a_0 + n * b)
< 0.1
That is the same as
(a_0 / b) + n * b / b
= (a_0 / b) + n
> 10
(because 0.1 = 1 / 10 and 1/x > 1/y <=> y > x if x,y != 0)
Since you metion in the comments that your actual problem is more complex: If finding a closed form solution like above is not feasible, look at this wikipedia page about fixed point iteration, which is exactly the kind of problem you try to solve.

Volume of pile of cubes

I'm trying a challenge. The idea is the following:
"Your task is to construct a building which will be a pile of n cubes.
The cube at the bottom will have a volume of n^3, the cube above will
have volume of (n-1)^3 and so on until the top which will have a
volume of 1^3.
You are given the total volume m of the building. Being given m can
you find the number n of cubes you will have to build? If no such n
exists return -1"
I saw that apparently:
2³ + 1 = 9 = 3² and 3 - 1 = 2
3³ + 2³ + 1 = 36 = 6² and 6 - 3 = 3
4³ + 3³ + 2³ + 1 = 100 = 10² and 10 - 6 = 4
5³ + 4³ + 3³ + 2³ + 1 = 225 = 15² and 15 - 10 = 5
6³ + 5³ + 4³ + 3³ + 2³ + 1 = 441 = 21² and 21 - 15 = 6
So if I thought, if I check that a certain number is a square root I can already exclude a few. Then I can start a variable at 1 at take that value (incrementing it) from the square root. The values will eventually match or the former square root will become negative.
So I wrote this code:
def find_nb(m):
x = m**0.5
if (x%1==0):
c = 1
while (x != c and x > 0):
x = x - c
c = c + 1
if (x == c):
return c
else:
return -1
return -1
Shouldn't this work? What am I missing?
I fail a third of the sample set, per example: 10170290665425347857 should be -1 and in my program it gives 79863.
Am I missing something obvious?
You're running up against a floating point precision problem. Namely, we have
In [101]: (10170290665425347857)**0.5
Out[101]: 3189089316.0
In [102]: ((10170290665425347857)**0.5) % 1
Out[102]: 0.0
and so the inner branch is taken, even though it's not actually a square:
In [103]: int((10170290665425347857)**0.5)**2
Out[103]: 10170290665425347856
If you borrow one of the many integer square root options from this question and verify that the sqrt squared gives the original number, you should be okay with your algorithm, at least if I haven't overlooked some corner case.
(Aside: you've already noticed the critical pattern. The numbers 1, 3, 6, 10, 15.. are quite famous and have a formula of their own, which you could use to solve for whether there is such a number that works directly.)
DSM's answer is the one, but to add my two cents to improve the solution...
This expression from Brilliant.org is for summing cube numbers:
sum of k**3 from k=1 to n:
n**2 * (n+1)**2 / 4
This can of course be solved for the total volume in question. This here is one of the four solutions (requiring both n and v to be positive):
from math import sqrt
def n(v):
return 1/2*(sqrt(8*sqrt(v) + 1) - 1)
But this function also returns 79863.0. Now, if we sum all the cube numbers from 1 to n, we get a slightly different result due to the precision error:
v = 10170290665425347857
cubes = n(v) # 79863
x = sum([i**3 for i in range(cubes+1)])
# x = 10170290665425347857, original
x -> 10170290665425347856
I don't know if your answer is correct, but I have another solution to this problem which is waaaay easier
def max_level(remain_volume, currLevel):
if remain_volume < currLevel ** 3:
return -1
if remain_volume == currLevel ** 3:
return currLevel
return max_level(remain_volume - currLevel**3, currLevel + 1)
And you find out the answer with max_level(m, 0). It takes O(n) time and O(1) memory.
I have found a simple solution over this in PHP as per my requirement.
function findNb($m) {
$total = 0;
$n = 0;
while($total < $m) {
$n += 1;
$total += $n ** 3;
}
return $total === $m ? $n : -1;
}
In Python it would be:
def find_nb(m):
total = 0
n = 0
while (total < m):
n = n + 1
total = total + n ** 3
return n if total == m else -1

How to loop a number so that the number iterates through every possible value?

I was doing Problem 9 in Project Euler when I ran into a problem. My program was taking way to long too run. More than half an hour. Here is my code.
def Problem9():
a = 1
b = 1
c = 1
x = []
while(a + b + c != 1000):
a = a + 1
for i in range(0,1000):
c = 1000 - (a + b)
if a < b < c:
if (a*a) + (b*b) == (c*c):
x.append(a*b*c)
print(a*b*c)
b = b + 1
print(x)
Problem9()
This basically is supposed to find out all the Pythagorean triplets which add up to one thousand(link to problem so that you can understand it better: https://projecteuler.net/problem=9) Is there a problem in my code which I can fix or is my code fundamentally wrong?
Since you know that the three numbers must add up to 1000, and a < b < c, you take advantage of that fact to loop much more efficiently (and cleanly).
def Problem9():
for a in range(1000):
for b in range(a,1000):
if a**2 + b**2 == (1000 - a - b)**2:
return a*b*(1000 - a - b)
Here, you loop over a from 1 to 1,000. Since b must be greater than a, you then looper over b from a until 1,000. Then, since you know that 1000 = a + b + c, then c = 1000 - a - b, and you can test you Pythagorean condition without any more looping.
A Pythagorean triplet is a set of three natural numbers, a < b < c, for > which a2 + b2 = c2.
There exists exactly one Pythagorean triplet for which a + b + c = 1000.
This will work
def pythagorean_tiplet():
a = 1
while(a < 1000):
b = a + 1 # note this, b is starting from a + 1, since b starting from 0 is useless and will only add to the running time.
while (b < 1000):
result = a**2 + b**2
c = math.sqrt(result)
if (( a + b + c) == 1000 and (a < b < c)): #test for conditions
return a * b * c
b += 1
a += 1
print(pythagorean_tiplet())
This
algorithm is definitely unsuitable for perimeters s > 1 000 000.
There is a faster algorithm that can be used to solve it. you can search for parametrisation of Pythagorean triplets
You've the system
(*1) a + b + c = 1000
(*2) a² + b² = c²
If
a + b + c = 1000
then
a + b = 1000 - c
(a + b)² = (1000 - c)²
a² + 2ab + b² = 1000² - 2000c + c²
( a² + b² ) + 2ab = 1000² - 2000c + c²
but, by the (*2), ( a² + b² ) = c², and then
c² + 2ab = 1000² - 2000c + c²
2ab = 1000² - 2000c
2000c = 1000² - 2ab
then
c = 500 - ab/(1000)
So, now, you've the new system:
(*3) a + b + 500 - ab/(1000) = 1000
(*4) c = 500 - ab/(1000)
Besides, a, b, and c are whole numbers, and a<b<c;
if a>332, a must be, at least, 333, and then,
b should be, at least, 334, and then, c should be, at least, 335; 333 + 334 + 335 = 1002.
With more math, you can do this even easier.
def p():
for a in range(1,333):
for b in range(a+1,(1000-a)/2):
if ( 1000*a + 1000*b + 500000 - a*b == 1000000 ):
c=500-((a*b)/1000)
print (a,b,c);print a*b*c
return
p()
Result:
time python Special_Pythagorean_triplet.py
(200, 375, 425)
31875000
real 0m0.041s user 0m0.036s sys 0m0.000s
In the if statement:
if ( 1000*a + 1000*b + 500000 - a*b == 1000000 )
you could use:
if ( a + b + 500 - (a*b)/1000 == 1000 )
but, in this case, only whole numbers matters:
with the first, you get around division and its rounding problems.
A better way is use itertools
https://docs.python.org/3.4/library/itertools.html
from itertools import product
def ff1():
for r in product(range(1,1000),repeat=3):
a,b,c=r
if a+b+c==1000:
if a<b<c:
if a**2+b**2==c**2:
print(a,b,c)
print(a*b*c)
ff1()
This code is really awkward. The while condition itself it's somehow wrong, you would stop with the first 3 numbers that sum 1000, then exit. Another wrong thing is that B doesn't reset. You can do similar to how Ibukun suggested, but it is not the best way to do for this direct approach. You DON'T need to check if they sum 1000. It's way simpler:
Iterate A from 3 to 997
Iterate B from A+1 to 999-A
Do C = 1000 - A - B (that's how you don't need to check the sum, you kinda already did that)
Check if they are triplet, when they are, you are done!
There are other great approaches you can check out once you enter the right answer, they are way more interesting

Categories