Basically, f(n)
is O(g(n))
then g(n)
is proportional to the worst-case scenario of f(x)
.
For example, binary search is O(log n)
(or O(ln n)
, which is equivalent). Why?
(Binary search works like this: take the middle element, and compare to the target. If it's the one, you're done. If it's bigger than the target, throw out the second half of the list and repeat on the first half; if it's smaller than the target, throw out the first half and repeat the search on the second half.)
Because you need 1 operation to find something in a list that is 3 elements long; 2 operations when it's 7 elements long; 3 if it is 15 elements long. Thus, when number of elements n
is (2^x - 1)
for any x
, the number of operations is x
; turn it around, and you'd say for number of elements n
, number of operations is log_2 n
. And say that each operation lasts 2 seconds (say you're comparing stuff by hand), and the worst time to search is log_2 n * 2 seconds
. log_2 n
can be rewritten as ln n / ln 2
, so the formula becomes:
worst search time(n) = (ln n / ln 2) * 2 seconds
= (2 seconds / ln 2) * ln n
Now, 2 seconds / ln 2
is a constant; let's call it c
. Let's call "search time for n elements" f(n)
. And let's call ln n
as g(n)
.
We said before, if n = 3
, g(3) <= c * ln 3
(because c * ln 3
is worst search time, real search time is always less or equal to that; but we could always find it on our first try). If n = 7
, g(7) <= c * ln 7
; etc.
The bit about n0
is just a guard that says the complexity we calculate for the small n
might be a deviation, an anomaly, an exception from the rule, and if we go with big enough data (i.e. n >= n0
), the rule becomes obvious and inviolate. In this case, the rule works pretty much from the start, but some algorithms might have extra costs that throw off the calculation on small numbers.