m4_divert(2)
m4_define(_commentnesting, 0)
m4_define(_begincomment,m4_dnl
m4_ifelse(_commentnesting, 0, ))
m4_define(_warning,m4_dnl
_begincomment()This document was created by gen-_name.html Do not edit this document, edit gen-_name.html._endcomment())
m4_define(_hdrs, )
m4_define(_anchor_no, 0)
m4_define(_abs, m4_ifelse(m4_eval($1 < 0), 1, m4_eval(-($1)), $1))
m4_define(hdr,
_warning()
,_warning()
$1
_warning())
m4_define(itl,_warning()$1_warning())
m4_define(bld,_warning()$1_warning())
m4_define(hrf,_warning()$2_warning())
m4_define(mailat,_warning()hrf(mailto:$1,$1)_warning())
m4_define(nwsgrp, hrf(news:$1, $1))
m4_define(bookref, $1, $2. $3: $4, $5.)
m4_define(i2m,m4_ifelse(
$1,01,January,
$1,02,February,
$1,03,March,
$1,04,April,
$1,05,May,
$1,06,June,
$1,07,July,
$1,08,August,
$1,09,September,
$1,10,October,
$1,11,November,December))
m4_dnl $Revision: 1.5 $, $Date: 1999/12/09 19:16:32 $
so that each of the squares contains an omicron, theta, or omega to indicate the asymptotic relation between the function labeling the row containing the square and the function labeling the column containing the square.
100itl(n)^2 log itl(n) 1.3^itl(n) sin itl(n) 100itl(n)^2 log itl(n) 1.3^itl(n) sin itl(n) ![]()
For example, the second square (in left to right order) on the top row would contain an omicron if 100itl(n)^2 is O(log itl(n)), or it would contain a omega if 100itl(n)^2 is Omega(log itl(n)), or it would contain a theta if 100itl(n)^2 is Theta(log itl(n)).
You should put the strongest asymptotic relation possible in the squares; for example, it would be wrong to conclude that itl(f)(itl(n)) is itl(O)(itl(g)(itl(n))) when itl(f)(itl(n)) is Theta(itl(g)(itl(n))). You only need to deal with the big asymptotic relations big omicron, big theta, and big omega; you may ignore the little asymptotic relations little omicron and little omega.
We can start with the observation that, for itl(c) = 1 and itl(N) = 0, itl(f) <= itl(f)(itl(n)) for all itl(n) >= 0, and so itl(f) = itl(O)(itl(f)). Similarly, itl(f) >= itl(f)(itl(n)) for all itl(n), and itl(f) = Omega(itl(f)). Because itl(f) = itl(O)(itl(f)) and itl(f) = Omega(itl(f)), then itl(f) = Theta(itl(f)), and that takes care of the main diagonal:
Next, note that -1 <= sin itl(n) <= 1 for all values of itl(n). itl(n)^2, log itl(n), and 1.3^itl(n) are all monotone increasing functions and all are greater than 1 for itl(n) > 10 (assuming the base of the log is 10), so each function is an upper bound for sin itl(n), and sin itl(n) is a lower bound for each function:
100itl(n)^2 log itl(n) 1.3^itl(n) sin itl(n) 100itl(n)^2 log itl(n) 1.3^itl(n) sin itl(n) ![]()
Theorem 1 tells us directly that itl(n)^2 = itl(O)(1.3^itl(n)), or
100itl(n)^2 log itl(n) 1.3^itl(n) sin itl(n) 100itl(n)^2 log itl(n) 1.3^itl(n) sin itl(n) ![]()
itl(n)^2 <= c1.3^itl(n)for some c > 0 and any n >= N. Multiplying through by 100 gives
100itl(n)^2 <= c'1.3^itl(n)where c' = 100c, and so 100itl(n)^2 = itl(O)(1.3^itl(n))
Finaly, knowing that log itl(n) = itl(O)(itl(n)) and using transitivity fills in the rest of the table:
100itl(n)^2 log itl(n) 1.3^itl(n) sin itl(n) 100itl(n)^2 log itl(n) 1.3^itl(n) sin itl(n) ![]()
100itl(n)^2 log itl(n) 1.3^itl(n) sin itl(n) 100itl(n)^2 log itl(n) 1.3^itl(n) sin itl(n) ![]()
Working with an analogy from the big-letter notation, we might say that itl(f) is theta(itl(g)) if itl(f) is itl(o)(itl(g)) and itl(f) is omega(itl(g)), which is equivalent to saying that itl(g) is itl(o)(itl(f)). However, from the definition of itl(o)(), if itl(f) is itl(o)(itl(g)), then itl(g) can never be itl(o)(itl(f)) because itl(g) grows strictly faster than itl(f) - otherwise the limit itl(f)/itl(g) would diverge - which means the limit itl(g)/itl(f) does diverge, and so itl(g) is not itl(o)(itl(f)).
It is possible to define little theta, and the definition most commonly used is that itl(f) is theta(itl(g)) if, as n tends towards infinity, the ratio itl(f)/itl(g) tends toward one.
We want to define itl(T)(itl(n)), the amount of work done by the algorithm when processing a complete binary tree having itl(n) levels. Using the recursive structure of trees, a complete binary tree having itl(n) levels consists of a root node with two subtrees, each of which is a complete binary tree having itl(n) - 1 levels.
The amount of work done by the algorithm when processing an itl(n)-level complete binary tree of follows directly from the recursive structure of trees: it does one unit of work for the root node, plus the amount of work it expends on the left subtree, plus the amount of work it expends on the right subtree, or
itl(T)(itl(n)) = 1 + itl(T)(itl(n) - 1) + itl(T)(itl(n) - 1) = 1 + 2itl(T)(itl(n) - 1)
To set the base of the recurrence, note that the algorithm expends 1 unit of work on the complete binary tree with 0 levels:
itl(T)(0) = 1
Probably the easiest way to solve the recurrence
is to unroll it a few times and then guess at a pattern. The value of itl(T)(itl(n) - 1) is
(1) itl(T)(itl(n)) = 1 + 2itl(T)(itl(n) - 1)
itl(T)(itl(n) - 1) = 1 + 2itl(T)(itl(n) - 2)which, when substituted back into equation (1) gives
A pattern seems to be emerging, but let's unroll it one more time just to be sure. itl(T)(itl(n) - 2) = 1 + 2itl(T)(itl(n) - 3), which, when substituted into equation (2), gives
(2) itl(T)(itl(n)) = 1 + 2(1 + 2itl(T)(itl(n) - 2)) = 1 + 2 + 4itl(T)(itl(n) - 2))
At this point it seems safe to guess that
itl(T)(itl(n)) = 1 + 2 + 4(1 + 2itl(T)(itl(n) - 3)) = 1 + 2 + 4 + 8itl(T)(itl(n) - 3))
where sum() is an alternative representation of the usual sigma notation; that is,
(3) itl(T)(itl(n)) = sum(i = 0 to itl(n), 2^i)
sum(i = 0 to itl(n), 2^i) = 2^0 + 2^1 + ... + 2^itl(n)
But we're not done yet. It's one thing to guess that equation (3) is true, it's quite another thing to prove it's true. The easiest way to prove equation (3) is true is by induction. The base case for the induction follows from the base case for the recurrence and a few simple identities:
itl(T)(0) = 1 = 2^0 = sum(i = 0 to 0, 2^i)
Now for the inductive step. Assume for some itl(j) >= 0
The value of itl(T)(itl(j) + 1) is, by the definition of the recurrence
(4) itl(T)(itl(j)) = sum(i = 0 to itl(j), 2^i)
Substituting equation (4) into equation (5) gives
(5) itl(T)(itl(j) + 1) = 1 + 2itl(T)(itl(j))
itl(T)(itl(j) + 1) = 1 + 2sum(i = 0 to itl(j), 2^i)Replacing sum() by its definition gives
itl(T)(itl(j) + 1) = 1 + 2(2^0 + 2^1 + ... + 2^itl(j))Carrying through the multiplication gives
itl(T)(itl(j) + 1) = 1 + 2^1 + 2^2 + ... + 2^(itl(j) + 1)Recognizing that 1 = 2^0, we can re-establish the sum() notation
and this is what we needed to prove.
(6) itl(T)(itl(j) + 1) = sum(i = 0 to itl(j) + 1, 2^itl(i))
At the risk of putting too fine a point on it, there's one more step we can take. Equation (6) is not particularly convenient to use because computing the sum() function is rather involved (although not difficult). Is it possible to find a equivalent but simpler form for the right-hand side of equation (6)?
Taking a tip from the approach we used to solve the recurrence, let's unroll the first few values of sum to see if there's a pattern
So it would seem that
sum(i = 0 to 0, 2^i) = 1 = 1 sum(i = 0 to 1, 2^i) = 1 + 2 = 3 sum(i = 0 to 2, 2^i) = 1 + 2 + 4 = 7 sum(i = 0 to 3, 2^i) = 1 + 2 + 4 + 8 = 15 sum(i = 0 to 4, 2^i) = 1 + 2 + 4 + 8 + 16 = 31
sum(i = 0 to itl(n), 2^i)= 2^(itl(n) + 1) - 1 As always, however, a guess is not a proof, for which we'll turn once again to induction. The base case when itl(n) = 0 is easy: sum(i = 0 to 0, 2^itl(i)) = 1 = 2 - 1 = 2^1 - 1 = 2^(0 + 1) - 1For the inductive step assume the inductive hypothesis holds for some itl(j) >= 0 and consider the value of sum(i = 0 to j + 1, 2^itl(i)):sum(i = 0 to j + 1, 2^itl(i)) = sum(i = 0 to j, 2^itl(i)) + 2^(itl(j) + 1)from the definition of sum(). Substituting the inductive hypothesis, which is true for itl(j), givessum(i = 0 to j + 1, 2^itl(i)) = 2^itl(j + 1) - 1 + 2^(itl(j) + 1)A little algebra givessum(i = 0 to j + 1, 2^itl(i)) = 2(2^(itl(j) + 1)) - 1 = 2^(itl(j) + 2) - 1which is what we needed to prove.Finely we can conclude that
itl(T)(itl(n)) = 2^(itl(n) + 1) - 1
- Analyze the function
and derive an asymptotic expression of its run time on an input array of length itl(n).sum_array(a) sum <- 0 i <- 1 do i <= |a| j <- a[i] do j > 0 sum <- sum + 1 j <- j - 1 od i <- i + 1 od return sumThe first thing to do is replace the simple statements with their asymptotic approximations, and determine how much work a loop itself does on each iteration:
This analysis assumes the array size operatorTheta(1) Theta(1) do i <= |a| Theta(1) Theta(1) do j > 0 Theta(1) Theta(1) Theta(1) od Theta(1) od Theta(1)|a|
can be performed in constant time, which is usually - but not always - a safe assumption (for example, ifa
is an array of characters and you're using the C computational model, then the array size operator isstrlen()
and would take Theta(n) time to perform).Next use sequence combination to simplify things a bit, folding the work done by each loop on each iteration into the work done by the loop body:
Theta(1) do i <= |a| Theta(1) do j > 0 Theta(1) od Theta(1) od Theta(1)Now we have to bound the number of iterations done by each loop, starting with the inner-most loop. Bounding the inner loop requires putting a bound on the value of
j
, which is equivalent to putting a bound on the value ofa[i]
.The problem statement doesn't say anything about the values contained in
a
, apart from the implicit assumption that they're some kind of numbers, so we have to appeal to our computational model for a bound. It seems reasonable to assume that there is a constant, maximum representable value larger than whicha[i]
cannot be, and therefore that the inner loop has a constant - and tight - bound on the number of iterations it could perform.There are a few interesting notes to point out before going on with the analysis. The first is that the tight Theta(1) bound on the number of iterations has a sizeable constant: for the usual 32-bit representation of integers, the constant is around 4 billion. This looks like a good place to use the Manber's suggested double-oh (or infinite) notation to indicate an upper bound with a possibly unacceptably large constant.
Second, the assumption of a constant maximum representable value holds for many different computational models. Even a computational model that supports arbitrary precision big-nums - such as many Lisp systems and the Unix desk calculator
dc
- can only represent numbers that fit into the machine's storage, resulting in a ridiculously large - but still constant - upper bound. An assumption that holds under a wide variety of models is usually considered a good assumption, although it may also be considered bad if it leads to ridiculous but legitimate results.In any event, back to the analysis. Replacing the inner loop with its multiplicative asymptotic estimate gives
which simplifies toTheta(1) do i <= |a| Theta(1) Theta(1)*Theta(1) Theta(1) od Theta(1)The outer loop is bounded by the size of the input array, that is, by n:Theta(1) do i <= |a| Theta(1) od Theta(1)Simplifying one more time gives a Theta(n) bound on execution time.Theta(1) Theta(n)*Theta(1) Theta(1)
This page last modified on 18 i2m(01) 2000. m4_dnl $Revision: 1.2 $, $Date: 1999/03/12 17:36:48 $ m4_divert(1) m4_changequote({, }) {m4_define(tblfcntnts, _toc_tableit(}_hdrs{) _toc_down(_toc_ilevel, _toc_level) ) m4_define(_toc_tableit, m4_regexp($1, \([0-9]+\)\([0-9]+\)\([^]+\)\(.*\), m4_ifdef(_toc_level, , m4_define(_toc_level, m4_eval(\2 - 1)) m4_define(_toc_ilevel, _toc_level)) _toc_entry(\1, \2, \3) _toc_tableit(\4)) ) m4_define(_toc_down, m4_ifelse(m4_eval($1 < $2), 1, _toc_down($1, m4_eval($2 - 1))) ) m4_define(_toc_entry, m4_ifelse(m4_eval($2 > _toc_level),1,
, m4_eval($2 < _toc_level),1,_toc_down($2, _toc_level)) _toc_tagit($1, $3) m4_define(_toc_level, $2) ) m4_define(_toc_tagit,
- m4_translit($2, , ,) ) }