In the classic text by Abelson/Sussman, Structure and Interpretation of Computer Programs, in Section 1.2.2 on tree recursion and the Fibonacci sequence, they show this image:
The tree-recursive process generated in computing for the 5th Fibonacci number
Then they write: "Notice that the entire computation of (fib 3) - almost half the work - is duplicated. In fact, it is not hard to show that the number of times the procedure will compute (fib 1) or (fib 0) (the number of leaves in the above tree, in general) is precisely Fib(n + 1)."
I understand that they're making a point about tree-recursion and how this classic case of the Fibonacci tree-recursion is inefficient because the recursive function calls itself twice:
The tree-recursive function for computing a Fibonacci number
My question is, why is it obvious (i.e. "not hard to show") that the number of leaves is equal to the next Fibonacci number in the sequence? I can see visually that it is the case, but I'm not seeing the connection as to why the number of leaves (the reduced down fib 1 and fib 0 calculations) should be an indicator for the next Fibonacci number (in this case 8, which is Fib 6, i.e. the 6th Fibonacci number, i.e. Fib n+1 where n is 5).
It is obvious how the Fibonacci sequence is computed - the sum of the previous two numbers in the sequence yields the current number, but why does the number of leaves precisely equal the next number in the sequence? What is the connection there (other than the obvious, that looking at it and adding up the 1 and 0 leaves does, in fact, yield a total count of 8 in this case, which is the next (6th) Fibonacci number, and so on)?
"Not hard to show" is harder than "obvious".
Use induction with two base cases.
Let's call the number of computations in Fib(x), Fib01(x).
Then,
Fib01(0) = 1 by definition, which is Fib(1)
Fib01(1) = 1 by definition, which is Fib(2)
Now assume that Fib01(k) = Fib(k+1) for k < n:
Fib01(n) = Fib01(n-1) + Fib01(n-2)
= Fib(n) + Fib(n-1)
= Fib(n+1) by definition
QED.
The number of n=1 clauses must be equal to fib(n), because that is the only place a non-zero number comes from, and if the sum of some number of 1s is equal to fib(n), there must be fib(n) of them.
Since fib(n+1) = fib(n) + fib(n-1), we just need to show that there are fib(n-1) leaves computing fib(0). It's less obvious to me how to show this, but perhaps it falls inductively out of the previous case?
Perhaps a simpler approach is to just do the whole thing inductively, then.
For our base cases:
N=0: there are fib(N+1)=fib(1)=1 leaves in the tree. Proof by inspection.
N=1: there are fib(N+1)=fib(2)=1 leaves in the tree. Proof by inspection.
Induction step: to compute fib(N) for an arbitrary N, we compute fib(N-1) once, and fib(N-2) once, and add their results. By induction, there are fib(N) leaves in the tree coming from our computation of fib(N-1), and fib(N-1) leaves in the tree coming from our computation of fib(N-2).
There are therefore fib(N) + fib(N-1) leaves in our overall tree, which is equal to fib(N+1). QED.
We can prove this by extrapolation.
The number of leaves for Fib(0) = 1.
The number of leaves for Fib(1) = 1.
Now, the expression Fib(2) is basically the sum of Fib(1) + Fib(0), i.e., Fib(2) = Fib(1) + Fib(0). So from the tree itself, you can see that the number of leaves for Fib(2) is equal to the sum of leaves in case of Fib(1) and Fib(0). Therefore, the number of leaves for Fib(2) is equal to 2.
Next, for Fib(3) the number of leaves will be sum of leaves for Fib(2) and Fib(1), i.e., 2 + 1 = 3
As you must have observed by now, this follows a pattern similar to Fibonacci series. Infact if we define the number of leaves for Fib(n) to be FibLeaves(n), then we can see that this series is Fib(n) shifted left by 1 space.
Fib(n) = 0, 1, 1, 2, 3, 5, 8, 13, 21, ..
FibLeaves(n) = 1, 1, 2, 3, 5, 8, 13, 21, ..
And thus, the number of leaves will be equal to Fib(n + 1)
Look at it this way:
It is true that we can generate a part of the fibonnaci sequence by picking any two consecutive terms from anywhere in the full fibonnaci sequence and following the rules of generating the next term
i.e Full fibonnaci sequence = 0,1,1,2,3,5,8,13,21....
So if I picked any two consecutive terms e.x 3 and 5, and followed the rules of generating the next term in the sequence, I would generate a part of the fibonnaci sequence
i.e Part of fibonnaci sequence = 3,5,8,13,21,34...
It is true that the number of leaves for a term is equal to the sum of the number of leaves of its two previous terms. This rule is the same as that for generating a term in the fibonnaci sequence
So lets try to get the number of leaves for the second term i.e number of leaves for zeroth term + number of leaves for first term
The number of leaves for the zeroth term and first term is 1
The number of leaves for the second term becomes 1 + 1 = 2
Now, 1,1 are consecutive terms from the full fibonacci sequence and the rule for getting the number of leaves is the same as the rule for getting a term in the fibonacci sequence
Nice question! It is immediately obvious (after a moment's thought), because the number of leaves in a binary tree node is the sum of respective numbers for its two branches, and, vacuously, 1 for leaves -- which is the definition of Fibonacci numbers ... with this specific shape of a tree.
Implicit in the above imprecise general statement is the proof by induction that
N(0) = 1
N(1) = 1
N(n+2) = N(n+1) + N(n)
which directly maps onto that statement, making it specific and concrete!
I am currently trying to solve the above recurrence relation but am having trouble trying to decipher the pattern an rewrite it as a sum. Could anyone help me out?
k >= 0. T(n<=2) = 1.
This recurrence relation was obtained from an algorithm I wrote to obtain a single sorted array from an array that is k sorted (meaning that every k'th element is in sorted order). This algorithm runs at most k times. Each time k is reduced by one and every k'th element is added to another array. Finally each array is merged using the merge from merge sort (n time). This algorithm is called recursively until k = 0, meaning we have found each sorted sub array.
I have a feeling that this is O(k*n), but I am not sure.
It might help to note that
n - n/k = ((k - 1) / k)n,
so your recurrence relation represents n decaying geometrically by a factor of (k-1)/k at each step. To see how much work is done, let a = (k-1)/k. Then the work done is upper-bounded by
n + an + a2n + a3n + ...
= n / (1 - a)
= n / (1 / k)
= nk.
So your total work is O(nk).
As a note, I haven’t checked whether the recurrence relation you have matches your code - I’m just showing the math here. :-)
Given an array of size n (1<=n<=200000) with each entry a[i] ( 1<=a[i]<=100), find all the number of subsequences that form Arithmetic progression.
Subsequences are the sequences where you can leave any number of elements in the original sequence.
For example, the sequence A,B,D is a subsequence of A,B,C,D,E,F obtained after removal of elements C, E and F. The relation of one sequence being the subsequence of another is a preorder.
I have written O(n^2) solution using DP. But n^2 = 10^10. So, It'll not get accepted.
Here is what I did.
Pseudocode:
for every element A[i]:
for every element A[k] such that k<i:
diff = A[i] - A[k] + 100: (adding 100, -ve differences A.P.)
dp[i][diff] += dp[i-1][diff] + 1;
for every element A[i]:
for every diff, d:
ans = ans + dp[i][d];
return ans;
This is giving correct output but TLE for 3 big cases.
P.S. Please suggest better solution..!!
Is divide and conquer optimization DP required here?? If yes, tell me how to build the solution.
I am confused on where the 2nd term [T(n) = 2T(n/2) + THETA(n)] is derived from when writing the recurrence equation for Merger Sort.
From the Coursera class, it was stated that the 2nd term is due to what occurs outside of the recursive calls. So my guess is because it is due to the 2 For Loops, each would go up to n/2, so the total would be counting to n:
function mergesort(m)
var list left, right
if length(m) ≤ 1
return m
else
middle = length(m) / 2
for each x in m up to middle
add x to left
for each x in m after middle
add x to right
left = mergesort(left)
right = mergesort(right)
result = merge(left, right)
return result
Any help would be appreciated.
Thanks
Yes, that's right. There's linear work done to iterate across the elements of the input list, distributing each element into either the left or right subarray. That accounts for the Θ(n) term in the recurrence.
Hope this helps!
My previous qs. was unclear so I am again putting it in clear terms.
I need an efficient algorithm to count the number of arithmetic progressions in a series. The number of elements in a single AP should be >2.
eg. if the series is {1,2,2,3,4,4} then the different solutions are listed below(with index numbers):
0,1,3
0,2,3
0,1,3,4
0,1,3,5
0,2,3,4
0,2,3,5
hence the answer should be 6
I am not able to code it when these numbers become large and size of array increases. I need an efficient algorithm for this.
First of all, you answer is incorrect. Numbers 2,3,4 (indexes also 2,3,4) form an AP.
Second, here is a simple brute force algorithm:
def find (vec,value,start):
for i from start to length(vec):
if vec[i] == value:
return i
return None
for i from 0 to length(vec) - 2:
for j from i to length(vec) - 1:
next = 2 * vec[j] - vec[i] # the next element in the AP
pos = find(vec,next,j+1)
if pos is None:
continue
print "found AP:\n %d\n %d\n %d" % (i,j,pos)
prev = vec[j]
here = next
until (pos = find(vec,next = 2*here-prev,pos+1)) is None:
print ' '+str(pos)
prev = here
here = next
I don't think you can do better than this O(n^4) because the total number of APs to be printed is O(n^4) (consider a vector of zeros).
If, on the other hand, you want to only print maximal APs, i.e., APs which are not contained in any other AP, then the problem becomes much more interesting...