Prove (2^n - 1) via induction - recursion

I have this program that, when run, has prints in this pattern:
a(0) = 0
a(1) = 1
a(2) = 2 + a(1) = 3
a(3) = 3 + a(2) + a(1) = 3 + 3 + 1 = 7
a(4) = 4 + 3 + 3 + 1 = 15
At this point I observe that there is a pattern of it becoming O(2^n - 1). However, I am not sure if this is a valid proof by induction. An idea I had was:
a(n)= n + a(n-1) + a(n-2) + ... + 1 = 2^n - 1
But from here the pattern for the terms are not very clear to me. I know the first term is n (in our code, this is due to a for loop that prints a statement n times), and due to recursion I know I will be summing the previous values but I don't know what a(n-1), a(n-2), etc. are in terms of n.

Related

Substitution method for solving reccurences

I have recently had trouble with understanding the substitution method for solving reccurences. I watched few on-line lectures about the problem, but sadly it did not tell me much (in one of them I heard that it is based on guessing, which made me even more confused) and I am looking for some tips. My objective is to solve three different reccurence functions using substitution method, find their time complexity and their values for T(32).
Function 1 is defined as:
T(1) = 1
T(n) = T(n-1) + n for n > 1
I started off by listing first few executions:
T(2) = T(2-1)+2 = 1+2
T(3) = T(3-1)+3 = 1+2+3
T(4) = T(4-1)+4 = 1+2+3+4
T(5) = T(5-1)+5 = 1+2+3+4+5
...
T(n) = 1+2+...+(n-1)+n = n(n+1)/2
Then I proved by induction, that T(1) = 1 using the formula for sum of the first n natural numbers, and then that it is also true for n+1. It was pretty clear to me, but I am not sure whether this is substitution method. Also knowing the formula T(n) = n(n+1)/2 I easily calculated T(32) = 528 and counted the time complexity, which is O(n^2).
In examples (2) and (3) I only need solution for n=2^k when k is a natural number, but it would be nice if you recommended me any articles showing how to get these for all n as well (but I suppose it is way harder than that).
Function 2 is defined as:
T(1) = 0
T(n) = T(n/2) + 1 for even n > 1
T(n) = T((n+1)/2) + 1 for odd n > 1
As I was allowed to prove it only for n=2^k and based on my gained knowledge I tried to do it following way:
T(n) = T(n/2) + 1
= T(n/4) + 1 + 1 = T(n/4) + 2
= T(n/8) + 1 + 2 = T(n/8) + 3
= T(n/16) + 1 + 3 = T(n/16) + 4
= T(n/2^i) + i // where i <= k, according to tutorials
And this is the moment where I get stuck and I cannot proceed further. I suppose that my calculations are correct, but I am not sure how should I look for a formula, which would satisfy this function. After I get the right formula, calculating T(32) or time complexity will not be a problem.
Function 3 is defined as:
T(1) = 1
T(n) = 2T(n/2) + 1 for even n > 1
T(n) = T((n – 1)/2) + T((n+1)/2) + 1 for odd n > 1
My calculations:
T(n) = 2T(n/2) + 1
= 2(2T(n/4)+1) + 1 = 4T(n/4) + 3
= 4(2T(n/8)+1) + 3 = 8T(n/8) + 7
= iT(n/2^i) + 2^i - 1
And again it comes to the formula, which I am not sure how should be rewritten.
Basically, does substitution method for solving reccurences means finding and iterative formula?
After restudying the topic I found what I did wrong and not to leave my question unanswered, I will try to do it.
The first function is calculated well, induction proof is also correct - nothing to add here.
When it comes to the second function where I got stuck, I did not
pay attention that I was actually using a substitution n=2^k. This
is how it should look:
T(n) = T(n/2) + 1
= T(n/4) + 1 + 1 = T(n/4) + 2
= T(n/8) + 1 + 2 = T(n/8) + 3
= T(n/16) + 1 + 3 = T(n/16) + 4
= T(n/2^k) + k
= T(1) + k
= k
Induction proof that T(2^k) = k works:
Base case: k=1, then T(2^1) = T(2) = 1. (it cannot be k=0, as 2^0 is not bigger than 1)
Inductive step: assume T(2^k) = k, we want to show T(2^(k+1)) = k+1. As 2^k=n, then 2^(k+1) = 2*2^k = 2n.
T(2n) = T(n) + 1
= T(n/2) + 1 + 1
= T(n/4) + 2 + 1
= T(n/8) + 3 + 1
= T(1) + k + 1
= k + 1.
Time complexity: O(log n)
T(32) = T(2^5) = 5
In the third function I missed that every time the function called
itself, the value has doubled.
T(n) = 2T(n/2) + 1
= 2(2T(n/4)+1) + 1 = 4T(n/4) + 3
= 4(2T(n/8)+1) + 3 = 8T(n/8) + 7
= 8(2T(n/16)+1) + 7 = 16T(n/16) + 15
= 16(2T(n/32)+1) + 15 = 32T(n/32) + 31
= 2^k*T(n/2^i) + 2^k - 1
= 2^k*T(1) + 2^k - 1
= 2^k + 2^k - 1
= 2^(k+1) - 1
Induction proof that T(2^k) = 2^(k+1)-1 works:
Base case: k=1, then T(2^1) = T(2) = 3. Original function T(2) = 2T(1)+1 = 2+1 = 3, so base case is true.
Inductive step: assume T(2^k) = 2^(k+1)-1, we want to show T(2^(k+1)) = 2^(k+2)-1. Similarly, as in the second function, 2^k=n, so 2^(k+1) = 2*2^k = 2n.
T(2n) = 2T(n) + 1
= 2(2T(n/2)+1) + 1 = 4T(n/2) + 3
= 4(2T(n/4)+1) + 1 = 8T(n/4) + 7
= 8(2T(n/8)+1) + 1 = 16T(n/8) + 15
= 2^(k+1) + 2^(k+1) - 1
= 2*2^(k+1) - 1
= 2^(k+2) - 1.
We could also take a look at first few elements for T(n), which are 1, 3, 5, 7, 9, etc., so T(n) = 2n-1
Time complexity: O(n)
T(32) = T(2^5) = 2^(5+1) - 1 = 64 - 1 = 63

Run Time Estimate/Big O Notation for Nested For Loop

I'm having trouble understanding the meaning of a function f(x) representing the number of operations performed in some code.
int sum = 0; // + 1
for (int i = 0; i < n; i++)
for (int j = 1; j <= i; j++)
sum = sum + 1; // n * (n + 1) / 2
(Please note that there is no 2 in the numerator on that last comment, but there is in the function below.)
Then my notes say that f(x) = 2n(n + 1) / 2 + 1 = O(n^2)
I understand that because there are two for loops, that whatever f(x) is, it will be = O(n^2), but why is the time estimate what it is? How does the j<= i give you n*(n+1)? What about the 2 in the denominator?
Think about, across the entire execution of this code, how many times the inner loop will run. Notice that
when i = 0, it runs zero times;
when i = 1, it runs one time;
when i = 2, it runs two times;
when i = 3, it runs three times;
...; and
when i = n - 1, it runs n - 1 times.
This means that the total number of times the innermost loop runs is given by
0 + 1 + 2 + 3 + 4 + ... + (n - 1)
This is a famous summation and it solves to
0 + 1 + 2 + 3 + 4 + ... + (n - 1) = n(n - 1) / 2
This is the n - 1st triangular number and it's worth committing this to memory.
The number given - n(n + 1) / 2 - seems to be incorrect, but it's pretty close to the true number. I think they assumed the loop would run 1 + 2 + 3 + ... + n times rather than 0 + 1 + 2 + ... + n - 1 times.
From this it's a bit easier to see where the O(n2) term comes from. Notice that n(n - 1) / 2 = n2 / 2 - n / 2, so in big-O land where we drop constants and low-order terms we're left with n2.

Time complexity of doubly recursive function

So this is the code:
int test ( int n)
{
if (n ≤2) return 1;
else return test(n-2) * test(n-2);
}
I'm not confident in how to reason about this recursive function. I tried mapping the N value to the recursion depth like so:
N = 2 -> 0 recursions
N = 4 -> 2
N = 8 -> 14
But to be honest I'm not sure this gets me anywhere (and just thinking about test(16) hurts my head.
Let's start by writing out a recurrence relation for the total number of calls made:
T(0) = T(1) = T(2) 1, since there's one total call (the initial call).
T(n+2) = 2T(n) + 1, since there's one call for the initial call, plus two recursive calls to problems of size n.
Let's start by looking at the case where n is even. Then
T(0) = 1
T(2) = 1
T(4) = 2T(2) + 1 = 3
T(6) = 2T(4) + 1 = 7
T(8) = 2T(6) + 1 = 15
T(9) = 2T(8) + 1 = 31
Except for the 0 case, it looks like these values take on the pattern 1, 3, 7, 15, 31, etc. Notice that each of these is one less than a power of two: 1 = 2 - 1, 3 = 4 - 1, 7 = 8 - 1, etc. We can guess that what we're looking at has something to do with powers of two.
Going back to our sequence, we might make a guess that
T(2) = 1 = 21 - 1
T(4) = 3 = 22 - 1
T(6) = 7 = 23 - 1
...
T(2n) = 2n - 1
So if n is even, we have T(n) = 2n/2 - 1 = (√2)n - 1. You can formalize this using a proof by induction.
For the odd case, we basically get the same thing:
T(1) = 1
T(3) = 2T(1) + 1 = 3
T(5) = 2T(3) + 1 = 7
T(7) = 2T(5) + 1 = 15
T(9) = 2T(7) + 1 = 31
...
T(2n+1) = 2n - 1
So if n is even, then T(n) = 2(n-1)/2 - 1. Again, you can prove this by induction to formalize things if you'd like.

How can I calculate the exact worst-case running time of a function given by a recurrence?

I am trying to calculate the value of the running time at the worst case for a function whose worst-case runtime is given by this recurrence:
T(0) = 0
T(n) = n + T(n - 1) (if n > 0)
Does anyone have any advice how to do this? I don't see how to solve this.
It might help to try expanding out the recurrence to see if you spot a pattern:
T(0) = 0
T(1) = 1 + T(0) = 1 + 0
T(2) = 2 + T(1) = 2 + 1 + 0
T(3) = 3 + T(2) = 3 + 2 + 1 + 0
Based on this pattern, it looks like T(n) = 0 + 1 + 2 + ... + n. This is a famous summation worked out by Gauss, and it solves to n(n+1)/2. Therefore, we could conjecture that T(n) = n(n+1)/2.
You can formalize this by proving it by induction. As a base case, T(0) = 0 = 0(0+1)/2, so everything checks out. For the inductive step, assume T(n) = n(n+1)/2. Then T(n+1) = (n+1) + T(n) = (n+1) + n(n+1)/2 = (n+1) (1 + n / 2) = (n+1)(n+2)/2 = ((n+1))((n+1) + 1) / 2, which checks out as well.
Therefore, your exact value is T(n) = n(n+1)/2.
Hope this helps!

Solving the recurrence relation for number of nodes in an AVL tree?

Suppose that we have this recurrence relation, which comes up in the analysis of AVL trees:
F1 = 1
F2 = 2
Fn = Fn - 1 + Fn - 2 + 1 (where n ≥ 3)
How would you solve this recurrence to get a closed-form for F(n)? This number is used to get the mininum number of internal nodes in an AVL tree with height n.
There are many ways that you can solve this recurrence, but most of them are pretty involved. I think the easiest way to do this would be to expand out a few terms of the series and see what you find:
F(1) = 1
F(2) = 2
F(3) = 4
F(4) = 7
F(5) = 12
F(6) = 20
If you take a look at this, you'll note that the following holds:
F(1) = 1 = 2 - 1
F(2) = 2 = 3 - 1
F(3) = 4 = 5 - 1
F(4) = 7 = 8 - 1
F(5) = 12 = 13 - 1
F(6) = 20 = 21 - 1
It looks like these terms are just terms from the Fibonacci series with 1 subtracted from them. In particular, note that F3 = 2, F4 = 3, F5 = 5, etc. Therefore, it looks like the pattern is
F(1) = 2 - 1 = F3 - 1
F(2) = 3 - 1 = F4 - 1
F(3) = 5 - 1 = F5 - 1
F(4) = 8 - 1 = F6 - 1
F(5) = 13 - 1 = F7 - 1
F(6) = 21 - 1 = F8 - 1
So, more generally, it looks like the pattern is F(n) = Fn + 2 - 1. We could try to formalize this using mathematical induction:
Base cases:
F(1) = 1 = 2 - 1 = F3 - 1
F(2) = 2 = 3 - 1 = F4 - 1
Inductive step: Assume F(n) = Fn+2 - 1 and F(n + 1) = Fn+3 - 1. Then we have that
F(n + 2) = F(n) + F(n + 1) + 1 = Fn+2 - 1 + Fn+3 - 1 + 1 = (Fn+2 + Fn+3) - (1 + 1) + 1 = Fn+4 - 1 = F(n + 2) + 2 - 1
Et voila! The induction checks out.
So how did I think to look for that pattern with the Fibonacci series? Well... the recursive definition kinda looked like the one for the Fibonacci series, so I expected there was probably some kind of connection between the two of them. Making the observation that everything was a Fibonacci number minus one was just creative insight. You could potentially try to solve this by using generating functions or other combinatorial tricks, though I'm not much of an expert on them.
Hope this helps!

Resources