2
$\begingroup$

I am pretty new to this, consider the following algorithm:

Calc_a(n):
    if n ==1: return 1
    else:
    sum = 0
    for i=1 to n-1:
        sum = sum + calc_a(i)
    return sum

So after running this algorithm a few times I see it basically finds the n'th element in a sequence defined by this recurrence relation:

$a_n = \sum_{i=1}^{n-1}a_i $

I need to find the recurrence relation for this algorithm running time per input. Walk me through it :)

Lets define running time as $T(n)$.
So the base case n=1 is $O(1)$ (theta?), as we just return 1.
Then we iterate (n-1) times , each time performing addition and calling the function on the current index, this is where I get confused.
I know how to deal with loops and recursion individually, not together.

My homework specifies that I should use constants rather than asymptotic notations. Does that means I should define the running time of Calc_a(i) as some constant?

How should I go about this?

*** EDIT ****
The answer I've come up with:

$ T(n) = \begin{cases} 1, & \text{if $n = 1$} \\ \sum_{i=1}^{n-1}( T(i) + B) & \text{else} \end{cases} $

For some constant B.

$\endgroup$
1
$\begingroup$

For starters, there are a few ways to prove recursive complexity. Some helpful notes can be found all over. For this problem, we can think about the recursion tree. The root of the tree is the Calc_a(n) call, and it's children are all $n-1$ nodes for each Calc_a(i) call initiated by the original Calc_a(n) call. For each of it's children, we can define the subtree rooted at them in the same manner. Our first step will be to determine how many nodes are in this tree.

Lets for some call Calc_a(n), lets see how many times Calc_a(i) is called for some $i<n$. This is the same as counting all Calc_a(i) nodes in the tree defined above. Let's first notice a pattern.

  • Calc_a(n-1) is only called once, that is clear.
  • Calc_a(n-2) will be called by the original Calc_a(n) call as well as the Calc_a(n-1). 2 calls
  • What about Calc_a(n-3)? Well we have 1 from the original call, and we can see that there will be two calls from Calc_a(n-1), and one more from Calc_a(n-2) 4 calls
    • Why are there two calls from Calc_a(i-1)? Well, in the previous bullet point we see that for any $i$, Calc_a(i-2) will be called twice for every Calc_a(n). We can use that logic here.
  • Calc_a(n-4)? We have one from the original call. As we can see in the previous bullet point, there will be 4 resulting calls to Calc_a(n-4) from Calc_a(n-1), 2 from Calc_a(n-2), and 1 more from Calc_a(n-3) 8 calls
    • Again we use the fact that the previous bullet point showed that there are 4 resulting calls to Calc_a(i-3) from any Calc_a(i) call.

We notice the pattern unfold that any Calc_a(i) is called twice as many times as Calc_a(i+1). Therefore Calc_a(i) is called $2^{n-i-1}$ times.

$$\sum_{i=1}^{n-1}2^{n-i-1}=\sum_{i=0}^{n-2}2^{i}=2^{n-1}$$ (I'm not sure if you're expected to include the original Calc_a(n) call, if you are, just add 1)

Proof Method 1

Now, as far as $T(n)$ goes, we can define it as $T(n)=f(n)+\sum_{i=1}^{n-1}T(i)$ where $f(n)$ is the work done in the function not including the recursive calls. As I'm sure you can tell, this is just a constant, so each node in the work tree does constant work. So we've determined the number of nodes in the tree. This means that the total work of the tree is simply $T(n)\approxeq 2^{n-1}$ or $T(n)=\theta(2^{n-1})$.

You may be wondering why the sum doesn't contribute to the complexity. Well, we could look at the loop and include the sum, then $T(n)=f(n)+\sum_{i=1}^{n-1}(g(i) + T(i))$ where $g(i)$ is the complexity of computing the sum from $T(i)$. However, we can just consider $g(i)$ as a constant as well, since most arithmetic operations can be theoretically considered constant time (this is definitely not always the case, but that is a whole nother topic). Because the constant complexity of the addition is dominated by the complexity of the accompanying $T(i)$ call, we arrive at the same complexity.

Proof Method 2

A more formal method of proving this, and one that may be required (from my experience) on an exam or homework, is to use induction. Generally, the Method 1 above is useful for getting a guess on the complexity. Once you have a good guess, you use induction to prove it, as suggested in the comments below.

Claim: $T(n)=\theta(2^{n-1})$

Base case: $T(1)=1$ (we can also see that $T(2)=2$ and $T(3)=4$) (Notice we are counting the number of calls to Calc_a()

Inductive step \begin{align*} T(n+1) & = f(n+1) + \sum_{i=1}^n ( g(i) + T(i)) \\ & = f(n+1) + \sum_{i=1}^{n-1} (g(i) + T(i)) + (T(n) + g(n)) \\ & = f(n+1) + T(n) - f(n)+ T(n) + g(n)\\ & = 2T(n) + g(n) \\ & \leq 2^n+c \quad \text{for some } c\geq g(n) \end{align*} Where the last line follows from the inductive hypothesis and the fact that $g(n)$ is a constant i.e. $g(n)=O(1)$.

$\endgroup$
  • 1
    $\begingroup$ Using the last equality for $T(n)$, we get: $ T(n+1) = f(n+1) + \sum_{i=1}^n ( g(i) + T(i)) = f(n+1) + \sum_{i=1}^{n-1} (g(i) + T(i)) + (T(n) + g(n)) = f(n+1) + T(n) + T(n) + g(n) = 2T(n) + g(n) + f(n+1)$. To conclude the runtime is $2^{n-1}$ I need to get to the equation $T(n+1) = 2T(n)$, and I can't seem to see how this happens. $\endgroup$ – Tegernako Apr 20 at 17:44
  • $\begingroup$ To clairfy, you are agreeing with me, yes? I should add this into my answer. That's the inductive way of proving the runtime (mine was simply a handwavy explanation). $\endgroup$ – Bryce Kille Apr 20 at 17:49
  • $\begingroup$ I am :) I see the logic and intuition, getting lost in writing it properly.. $\endgroup$ – Tegernako Apr 20 at 17:50
  • $\begingroup$ I was asked to show this by comparing T(n+1) and T(n), thats why I try to show this directly.. $\endgroup$ – Tegernako Apr 20 at 17:51
  • 1
    $\begingroup$ Gotcha. I wholeheartedly appreciate your help, going through data structures courses is really demanding. Have a great week! $\endgroup$ – Tegernako Apr 20 at 18:14
0
$\begingroup$

You have already found the recurrence relation which, slightly corrected, yields $$ a_n \leq \sum_{i=1}^{n-1} (a_i + B) + C. $$ Let's solve instead what you wrote, $$ a_n = \sum_{i=1}^{n-1} a_i. $$ In order to solve this, let $b_n = \sum_{i=1}^n a_i$. Then $$ b_n = a_n + \sum_{i=1}^{n-1} a_i = 2\sum_{i=1}^{n-1} a_i = 2b_{n-1}. $$ Therefore $b_n = 2^n A$ for some constant $A$ Finally, $$ a_n = b_n - b_{n-1} = 2^{n-1} A. $$

$\endgroup$
  • $\begingroup$ Here, C represents the running time of the loop and recursion on each i? Shouldn't it be inside the sum then? $\endgroup$ – Tegernako Apr 20 at 14:45
  • $\begingroup$ Right, you actually need two constants, and it's actually only an inequality. $\endgroup$ – Yuval Filmus Apr 20 at 14:47
  • $\begingroup$ On second thought. We dont really need C here, as we cover the base case when i is equal to 1. So we should just write $ T(n) = \sum_{i=1}^{n-1}(T(i) +B) $? Am I right? $\endgroup$ – Tegernako Apr 20 at 15:38
  • $\begingroup$ The $C$ covers all other statements in the procedure’s body. $\endgroup$ – Yuval Filmus Apr 20 at 15:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.