5.1 Upper bounds: the big- notation
Several terms are used to describe the running-time equation for an algorithm. These terms – and their associated symbols – indicate precisely what aspect of the algorithm’s behaviour is being described. One is the upper bound for the growth of the algorithm’s running time. It indicates the upper or highest growth rate that the algorithm can have.
Because the phrase “has an upper bound to its growth rate of ” is long and often used when discussing algorithms, we adopt a special notation, called big- notation. If the upper bound for an algorithm’s growth rate (for, say, the worst case) is , then we would write that this algorithm is “in the set in the worst case” (or just “in in the worst case”). For example, if grows as fast as (the running time of our algorithm) for the worst-case input, we would say the algorithm is “in in the worst case”.
5.1.1 Formal definition
So, how do we define the upper bound? First, if is an upper bound of , then this should mean something like in the long run. What we mean by this is that whenever becomes sufficiently large, then should not outgrow .
But this is not all there is to it. We have already mentioned that we want to abstract away from constant factors – if algorithm is twice as fast as algorithm , then they grow at the same rate, and we want our notation to capture that. So what we want to say is that , for some arbitrary constant . This gives us the following formal definition:
- Upper bound
-
iff there exist positive numbers and such that for all
The constant is the smallest value of for which the claim of an upper bound holds true. Usually is small, such as 1, but does not need to be. You must also be able to pick some constant , but it is irrelevant what the value for actually is. In other words, the definition says that for all inputs of the type in question (such as the worst case for all inputs of size ) that are large enough (i.e., ), the algorithm always executes in less than or equal to steps for some constant .
Note that the definition is somewhat simplified, it only works if and are monotonically increasing. This means that if then , so the value can never decrease whenever increases. But this is not a real restriction for our purposes, because there are no algorithms that becomes faster when the input size grows. In the very best case, the runtime of an algorithm can be independent of the input size, but this is also monotonically increasing. If we were to allow any non-monotonic functions, then the definition of upper bound would become slightly more complicated. You can look up the formal definition in mathematical textbooks, or in Wikipedia.
Example: Comparing two functions
Assume and . How can we use the definitions above to prove that ?
We have to find positive numbers and so that . Since has a constant factor of 0.001, we can try with :
Now we readily see that is smaller than for all , so we can set .
Note that there are plenty of possible values to choose from, such as and . We can even use very large values such as , since we are only interested in what happens when grows infinitly large.
Example: Quadratic algorithm
For a particular algorithm, in the average case where and are positive numbers. Then,
for all . So, for , and . Therefore, is in by the definition.
If someone asked you out of the blue “Who is the best?” your natural reaction should be to reply “Best at what?” In the same way, if you are asked “What is the growth rate of this algorithm”, you would need to ask “When? Best case? Average case? Or worst case?” Some algorithms have the same behaviour no matter which input instance of a given size that they receive. An example is finding the maximum in an array of integers. But for many algorithms, it makes a big difference which particular input of a given size is involved, such as when searching an unsorted array for a particular value. So any statement about the upper bound of an algorithm must be in the context of some specific class of inputs of size . We measure this upper bound nearly always on the best-case, average-case, or worst-case inputs. Thus, we cannot say, “this algorithm has an upper bound to its growth rate of ” because that is an incomplete statement. We must say something like, “this algorithm has an upper bound to its growth rate of in the average case”.
Knowing that something is in says only how bad things can be. Perhaps things are not nearly so bad. Because sequential search is in in the worst case, it is also true to say that sequential search is in . But sequential search is practical for large in a way that is not true for some other algorithms in . We always seek to define the running time of an algorithm with the tightest (lowest) possible upper bound. Thus, we prefer to say that sequential search is in . This also explains why the phrase “is in ” or the notation “” is used instead of “is ” or “”. There is no strict equality to the use of big- notation. is in , but is not in .
5.1.2 Simplifying rules
We introduced simplifying rules in Section 2.7.1, but repeat them here in compact form:
Rule | Simplification | |
---|---|---|
(1) | Transitivity | if and , then |
(2) | Constant | if , then |
(3) | Addition | if and , then |
(4) | Multiplication | if and , then |
Using these rules we can easily determine the asymptotic growth rate for many algorithms.
- Rule (2) says that any constant-time operation is .
- Rule (3) says that if you have a sequence of statements, you only need to consider the most expensive statement: .
- Rule (4) says that if you have a loop that repeats a statement a number of times, the total cost is the cost of times the number of iterations: .
- Rule (2) also says that if you repeat a statement a constant number of times, you can treat it as you only execute once.
5.1.3 big- and logarithms
One interesting consequence of asymptotic complexity is that the base of a logarithm becomes irrelevant:
The reason for this is that according to the logarithm laws, . But is a constant which we can ignore, so . Therefore we can just ignore the base and write . (Note that this does not hold for exponential growth – e.g., , but .)
Another consequence of the logarithm laws is that it doesn’t really matter if you take the logarithm from a linear, quadratic, cubic, or any power function:
The reason for this is that according to the logarithm laws, so the exponent becomes a multiplicative constant and can be ignored. However, taking the power of a logarithm cannot be ignored, so and are different complexity classes.