Skip to main content
Artificial Computational Intelligence

Algorithm Analysis: From Basic Operations to Asymptotic Notation

Published: 2026-08-09
Level: postgraduate
Audience: Postgraduate students learning algorithm analysis and asymptotic notation

Prerequisite Knowledge

This lecture builds on the following concepts from earlier lectures. If any feel unfamiliar, review the linked notes before proceeding.

Previously Covered in This Subject

  • The five properties of an algorithm — covered in Lecture 1 (Properties of an Algorithm).
  • Euclid's algorithm, experimental analysis, and the RAM model — covered in Lecture 1 (Euclid's Algorithm; Experimental Analysis and Its Limitations; The RAM Model and Primitive Operations).
  • The basic operation method — covered in Lecture 1 (The Basic Operation Method).
  • arrayMax and counting primitive operations — covered in Lecture 1 (Counting Primitive Operations: arrayMax).

This session turns the abstract idea of "how fast is an algorithm" into a precise, examinable craft. We begin with a recap of the five properties of an algorithm, then build the practical tool we will use all semester — the basic operation method — and drive it through worked examples (ArrayMax, matrix multiplication). The counting results lead to the central concept of the session, the order of growth, and then to the professional language that compares algorithms: asymptotic notation, with its big O, big omega, and big theta families, the formal definitions, worked proofs, and the little brothers little o and little omega. The session ends with the practical question of when an algorithm is worth calling correct at all.

2.1 The Five Properties of an Algorithm (Recap)

We begin with a recap of the five consolidated properties of an algorithm, because the whole course builds on this definition. An algorithm is a step-by-step procedure for solving a problem; last session we asked what makes a procedure an algorithm, and the answer was consolidated under exactly five headings. The class runs faster today, so these foundations matter — anyone who missed the previous session is at a real disadvantage, since everything that follows uses these terms.

Why does a whole recap session start with a definition you already heard once? Because every claim made from here on — "this algorithm is efficient", "this algorithm is correct", "this algorithm is finite" — is tested against these five headings. If the definition is fuzzy, every later argument is fuzzy too. The five properties are the contract an algorithm must sign before we are even allowed to analyze it.

2.1.1 The Five Properties

The five properties of an algorithm are input, output, definiteness, finiteness, and effectiveness. Each one pins down a requirement:

  • Input — the algorithm takes some input values from a specified set.
  • Output — it produces an output related to the input.
  • Definiteness — every step is precisely defined and unambiguous; there is no room for interpretation.
  • Finiteness — the algorithm ends after a finite number of steps.
  • Effectiveness — every operation can be carried out, in principle, by a person using pencil and paper in a finite amount of time.

The warning that came with the recap was sharp: when asked for the properties of an algorithm, do not give generic answers like "efficient" or "unambiguous" — those are loose descriptions, not the consolidated headings. The five specific terms above are what the course uses, and the exam expects them. Exam note: expect to be asked for the properties of an algorithm using these exact five headings — input, output, definiteness, finiteness, effectiveness.

Q: Is "efficient" one of the five properties of an algorithm?

A: No. The specific term is "effectiveness". The five properties are consolidated as input, output, definiteness, finiteness, and effectiveness. When asked for the properties, do not give generic answers like "efficient" — use the specific headings.

The vocabulary correction happened live, so it is worth recording in full. The student's answer "efficient" was not wrong in spirit — an efficient algorithm is a good algorithm — but it is the wrong heading. The property that says "each operation can actually be carried out, in principle, with pencil and paper in finite time" has a specific name: effectiveness. Efficiency (how little time or space an algorithm uses) is a performance question, and it belongs to the analysis topics that fill the rest of this session. Effectiveness is a structural question: it asks whether the operations are even executable at all. Mixing the two words loses the distinction the course needs later, when we say "the algorithm is effective but not efficient" — it works, but it is slow.

Pitfall — answering with the wrong property names. "Efficient", "unambiguous", "clear", "correct" are all plausible-sounding answers, and none of them is one of the five headings. The exact list is input, output, definiteness, finiteness, effectiveness — five words, in that family. A second trap: "definiteness" and "unambiguous" sound like the same idea, but the course's word is definiteness (every step precisely defined, no room for interpretation), so answer with the course vocabulary.

2.1.2 Euclid's Algorithm, Experimental Analysis, and the RAM Model

The recap covered the rest of the previous session. First, Euclid's algorithm — the ancient procedure for computing the greatest common divisor of two numbers — was examined to test whether it qualifies as an algorithm under the five properties; it does, and it is the running example of a procedure that satisfies every property. It takes two positive integers as input (input), returns their greatest common divisor (output), each division step is fully specified (definiteness), the remainder strictly shrinks so the loop must stop (finiteness), and every step is a plain arithmetic operation doable by hand (effectiveness).

Second, we revisited the two ways to study how fast an algorithm runs. The first is experimental analysis: we convert the algorithm into a program, execute the program, and measure the time each algorithm actually takes. The second is the RAM model (Random Access Machine model), a simplified, idealized computer on which we do not execute anything at all — instead we count the number of primitive operations the algorithm performs. Experimental studies had limitations (the machine, the language, the input data all distort the measurement), which is why we moved to the RAM model, and inside the RAM model we count primitive operations. Recall the vocabulary: a primitive operation is one of the small, fixed-cost operations the RAM machine can do — a basic arithmetic operation, a comparison, an assignment, and so on — and last session we had a specific list of examples of primitive operations.

The two approaches answer the same question — "how fast is this algorithm?" — in very different ways:

Dimension Experimental analysis RAM model
What you do Write a program, run it, measure wall-clock time Count primitive operations on paper
What distorts the result The machine, the language, the compiler, the input data Nothing — the model is idealized
Result type A number in seconds (machine-specific) A count as a function of input size
Reproducibility Different machines give different numbers Same count on every machine
Weakness Cannot compare across machines or predict future hardware Counting every primitive op is tedious

That last row is the bridge to the next section: counting every single primitive operation is too fine-grained to be practical. The course instead adopts a coarser tool — the basic operation method — which counts just one carefully chosen operation. That is exactly where Section 2.2 starts.

Recap: an algorithm is a procedure with exactly five properties — input, output, definiteness, finiteness, effectiveness. Two ways to measure speed: experimental analysis (run it) and the RAM model (count primitive operations). The RAM model wins because it strips away machine noise — but counting every primitive operation is impractical, which motivates the basic operation method next.

Real-world connection: the RAM-model philosophy — "analyze the algorithm, not the machine" — is the same thinking behind benchmark-free algorithm selection in industry. When a company compares two candidate algorithms, it does not want an answer that depends on which laptop ran the test; it wants a machine-independent comparison that stays valid when the software is deployed on different hardware. That is precisely what the order-of-growth analysis of the rest of this session provides.

2.2 The Basic Operation Method

Counting every primitive operation is too fine-grained. The method we will use throughout the course — the basic operation method — counts just one carefully chosen operation instead, and from that single count we derive the time complexity.

Why would anyone throw away most of the count? Imagine timing a cooking recipe by counting every single movement of your hands — every stir, every pinch, every step. You would get an exact number, but the effort is absurd, and most of the movements do not decide how long the recipe takes. One dominant task (the slowest roast in the oven) determines the finish time. The basic operation method applies that same idea to algorithms: find the one operation that dominates, count only it, and the analysis stays correct while the counting becomes practical.

2.2.1 What Is a Basic Operation?

A basic operation is the operation that contributes the most towards the running time of an algorithm — equivalently, the statement that executes the maximum number of times. Do not confuse basic operations with primitive operations; the two terms are different. Primitive operations are the small building blocks; the basic operation is one specific primitive operation (or one statement) that dominates the running time.

The working assumption is that the two characterizations agree: the statement that executes the maximum number of times is also the one that contributes most to the running time. There can be cases where they differ — where the statement that executes most often is cheap and a rarely-executed statement is very expensive. In that situation, the tie-breaker is explicit: consider the operation that contributes the most towards the running time of the algorithm.

Scope of the working assumption. The method assumes the two tests agree: the most-frequent statement is also the most-expensive one. This holds for the vast majority of textbook algorithms, where the inner-loop statement is both. It breaks in constructed cases — a statement executed a billion times but costing a nanosecond, versus a statement executed once but costing an hour. When that happens, the definition itself gives the tie-breaker: the basic operation is the one that contributes the most to the running time, not the one with the highest frequency. The frequency reading is the shortcut; the running-time reading is the law.

2.2.2 The General Steps for Non-Recursive Algorithms

The standard recipe for analyzing a non-recursive algorithm has five steps:

  1. Decide on the parameter that indicates the input size.
  2. Identify the algorithm's basic operation.
  3. Check whether the number of times the basic operation is executed depends only on the input size . This check matters because the time taken can depend on the type of input as well. Example: if the numbers are already sorted, some sorting algorithm barely works; if the numbers are completely unsorted, it works hard; if they are partially sorted, it takes some time in between. When the input type matters, you have to analyze the worst case, the average case, and the best case separately — a topic that comes later.
  4. Set up a summation that reflects the number of times the basic operation is executed. The typical shape is , where counts executions of the basic operation as a function of the input size .
  5. Simplify the summation using standard formulas (the arithmetic series , the series formula for , and so on).

The whole course's analysis style rests on this recipe, so the sequence — input size, basic operation, dependency check, summation, simplification — is worth memorizing.

The counting formula. In step 4, the summation is the workhorse. Written generally: the total count equals a sum of ones, one per execution of the basic operation:

Here (the count) is named after "count", is the input size, and the summation runs over the loop bounds of whatever loop controls the basic operation. Each term represents the single unit of constant time the basic operation takes on one execution. Step 5 then replaces the sum with a closed form, using standard series such as:

The first says "adding ones gives "; the second is Gauss's formula for .

Mini trace of the recipe on a trivial loop. Take a loop that runs the basic operation once per iteration, from to . Step 1: input size is . Step 2: the loop body is the basic operation. Step 3: the count depends only on — the loop always runs times regardless of data. Step 4: the summation is . Step 5: . The count is . The answer: . Sense-check: a loop from 1 to has exactly iterations, so counting executions is right.

Exam note: the five-step recipe is the skeleton of nearly every complexity answer this course: (1) input size , (2) basic operation, (3) dependency check — if the input type matters, split into worst/average/best case, (4) summation , (5) simplify with standard series. Write these steps in order in the exam; a correct answer with no reasoning gets cut.

Real-world connection: this "count one dominant operation" habit is exactly how profiling works in industry. Engineers do not instrument every line of a system; they profile to find the hot path — the one function that consumes 90% of the time — and optimize only that. The basic operation is the algorithmic version of the hot path: find it, count it, and you know where the time really goes.

2.3 Worked Example: ArrayMax

The first worked example is the familiar ArrayMax algorithm: given an array of numbers, find the largest. The algorithm walks through the array with a loop, comparing the current element with the best found so far and updating the best when it finds something larger.

2.3.1 Finding the Basic Operation

The loop runs the index from 1 to (the first element is the initial best, so there is nothing to compare at position 0). Inside the loop, two candidate statements compete for the title of basic operation: the comparison of the current element with the current best, and the assignment that updates the best when the comparison succeeds.

The comparison wins, and the reason is the professor's key teaching point:

Q: Is the basic operation the assignment statement?

A: No. The comparison is the statement that executes the maximum number of times. The assignment may not execute — when the comparison is false, the assignment is skipped. The comparison happens whether the outcome is true or false, so it always runs.

The general rule shown here: a statement that runs conditionally can be skipped, but the condition that decides it runs every time. So when you look for the statement that executes the maximum number of times, pick the comparison, not the assignment it guards.

The same logic repeats through the whole course. Any time you see if (condition) { do something }, the condition is the candidate basic operation, not the body of the if. The body might be skipped on some inputs; the condition is evaluated on every pass. This is why loop-based searches (like the element-uniqueness exercise of Section 2.5.4) almost always end up with the comparison as the basic operation.

2.3.2 Counting Executions: T(n) = n − 1

Input size is . Basic operation is the comparison in the loop. The count does not depend on the worst case or the best case: we do not check any condition about whether the first element is largest or the last element is largest — the loop marches through the entire array regardless of the order of the numbers, so every input of size triggers the same number of comparisons.

Let denote the number of comparisons — the time taken expressed as a function of the input size . The loop runs the comparison once per iteration, and the loop goes from to , so

The professor's phrasing while writing this: the sum's upper limit follows your loop condition — if the loop runs to , the count is ; here the loop runs to , so the count is . The middle step is the standard summation rule: number of terms = upper limit minus lower limit plus 1, giving . The sum of 's counts the constant time taken by the one basic operation per iteration — "there is only one statement which will take some constant time."

The result: the time taken by ArrayMax is — the simplest algorithm and the simplest calculation. In words: the loop runs n minus 1 times, so the count is n minus 1.

Worked example with real numbers. Run ArrayMax on the array , so .

  • The initial best is the first element: .
  • : compare with best → 9 is larger, update best to 9. (1 comparison)
  • : compare with best → 2 is not larger, no update. (1 comparison)
  • : compare with best → 7 is not larger, no update. (1 comparison)

Total comparisons: . Note that the assignment ran only once (at ), but the comparison ran all three times — exactly the argument from Section 2.3.1. The formula says . The answer: 3 comparisons. Sense-check: with elements, the first is free, each of the other needs exactly one comparison — so is right no matter what the numbers are.

2.3.3 What About the Increment?

A natural doubt: the loop counter also runs every iteration, so why do we not count it too? The professor's answer is a small but important lesson about what "count" means:

Q: Why not count the increment i = i + 1? It also executes every time.

A: The increment also runs about n times. Even if you count it, the basic operation count becomes 2 per iteration, but the time per operation is still constant. The order of the result does not change.

In other words, including the increment doubles the counted constant but leaves the function (or ) with the same order of growth. Because we measure how the count grows with , a constant factor of 2 changes nothing about the conclusion. This is the first taste of the order-of-growth idea developed in Section 2.6.

Pitfall — thinking "exactly one statement may be counted." Counting the increment is not a mistake that changes the verdict; it gives , which is still a linear function. The mistake would be to conclude "ArrayMax takes time" versus " time" as if they were fundamentally different. They are not — the constant factor is invisible at the order-of-growth level. A related trap: do not let this freedom become an excuse for sloppy counting; the count must still be an honest function of , just not an over-precisely-constant one.

Recap: ArrayMax's basic operation is the comparison (it always executes; the assignment can be skipped), and its count is . Even counting extra constant-time statements leaves the same linear order of growth. This is the template that matrix multiplication (Section 2.5) will blow up into .

Real-world connection: the "conditional statement can be skipped, the condition always runs" rule is why database query planners and compilers optimize the test in hot loops rather than the rare update inside the branch. The condition is paid for unconditionally, on every record or every iteration; the branch body is a bonus cost that many inputs never pay.

2.4 Worked Example: Statements with Different Timings

This hypothetical example isolates a different question: when statements have different costs, which one decides the running time? It is a deliberately artificial setup — the professor said not to take the numbers literally — but it is the cleanest way to build the intuition that the slowest statement rules.

2.4.1 The Setup and the Answer

Suppose an algorithm has five statements, each with a different time cost:

Statement Time taken
Statement 1 0.25 ms
Statement 2 0.5 ms
Statement 3 0.25 ms
Statement 4 1 ms
Statement 5 1.5 ms

(There was a sixth statement, ignored for the exercise.) Five statements with different timings — which one is the basic operation? The answer: statement 5, because it takes the most time (1.5 ms). Statement 4 at 1 ms is the runner-up, but the basic operation is the single statement that takes the most time, not the second-most.

Worked example: reading the timing table.

  • Statement 1: 0.25 ms — the fastest, along with statement 3.
  • Statement 2: 0.5 ms — twice statement 1's time.
  • Statement 3: 0.25 ms — tied for fastest.
  • Statement 4: 1 ms — the runner-up; four times as slow as statement 3.
  • Statement 5: 1.5 ms — the slowest; 50% slower than statement 4, and six times as slow as statement 1.

The basic operation is the single statement with the maximum time: statement 5 at 1.5 ms. Sense-check: every statement must finish before the algorithm is done, so the last one to finish — the slowest — sets the total time. Since 1.5 ms is the largest entry in the "Time taken" column, statement 5 is the answer, not statement 4 and not the sum.

2.4.2 Why Not All Statements? (The Parallel-Execution Intuition)

A student proposed that the basic operation should be the sum of all the statements. The professor rejected the sum outright and gave the parallel-execution analogy:

Q: Should the basic operation be the sum of all the statements?

A: No. We pick only the statement that takes the most time. Just assume the statements run in parallel: if five people do the same work, the work finishes when the last person completes it. Statement five takes the most time, so it decides the running time.

The mental model: if all statements execute in parallel, the job is done only when the slowest statement is done — "when statement 5 or when statement 4 is executed, that is the time taken by the algorithm." If you cannot digest the method, the professor's advice is to assume parallel execution of all statements and let the slowest one set the finish time.

A follow-up question pushed on the analogy itself:

Q: Why not statement 3? It is fast, at 0.25 ms.

A: Statement three is done early. By the time statement 4 is a quarter complete, statement 3 has already finished. The slowest statement decides when the whole work ends — that is statement five.

The numbers in the setup make the point concrete: 0.25 ms is a quarter of statement 4's 1 ms, so statement 3 finishes long before the bottleneck statement. "Come out of the box. Think outside the box" — that is the nudge that accompanied this exchange.

Then came the sharpest objection, which the professor turned into a foundational warning:

Q: In parallel execution, does the answer depend on the number of cores of the machine? On one core, it would be the sum of all.

A: That is the problem with missing the foundations. In algorithm analysis we ignore machine details — we do not know the number of cores, and we do not know the programming language. We think only from the algorithm perspective.

The deeper point: algorithm analysis deliberately happens before any machine or language exists. We do not know whether the company we work for will give us a one-core machine or a four-core machine, and we do not know which programming language we will be asked to code in. So none of that enters the analysis — the analysis is purely about the algorithm itself. Exam note: when asked which statement is the basic operation, the answer is the statement that takes the most time, never "the sum of all the statements."

Scope of the parallel-execution picture. The analogy assumes the statements run simultaneously and the job ends when the last one ends — this is a mental tool, not a claim about real hardware. Real machines may run statements in sequence, and on a single core the total time would indeed be the sum. That is precisely the professor's point: the analysis must not depend on such details, because the number of cores is unknown and may change. The parallel picture exists only to isolate the rule — the slowest statement decides — from machine noise.

Pitfall — answering "the sum of all statements." This is the single most common wrong answer for this kind of question, and the professor flagged it as an exam zero. A related trap is naming the second-most-expensive statement (statement 4 here): only the maximum counts. And never answer "the for loop" — a loop is not an operation at all (Section 2.5 drills this).

Recap + bridge: when statements have different costs, the basic operation is the single slowest statement — the one that takes the most time — because the whole algorithm waits for it. This completes the basic-operation method's "which statement?" question; next we scale the same method to nested loops, where the count becomes a power of .

Real-world connection: the slowest-statement rule is the principle behind bottleneck analysis in manufacturing and software systems — a production line is only as fast as its slowest station, and a web request is only as fast as its slowest dependency (database query, external API, image server). Engineers find the bottleneck and fix that one component; speeding up the already-fast parts changes nothing. Same rule, same reason.

2.5 Worked Example: Matrix Multiplication

The second worked example is matrix multiplication — the classic case where the counting produces a power of , and the classic lesson in interpreting what means.

2.5.1 The Algorithm and the Shape Rule

The algorithm multiplies two square matrices and , each of order , and produces the product matrix , also . The professor flagged the shape rule so nobody confuses this special case with the general rule: normally, if is of order and is of order , we can multiply them and the result is of order . Here everything is — both factors square, so the general rule collapses to the square case. Do not confuse the two.

The algorithm, in pseudocode, is the definition-based triple loop:

The outer two loops initialize the cells of ; the inner loop accumulates the dot product. All three loops run from 0 to .

2.5.2 Counting: T(n) = n³

Input size is . Which statement is the basic operation? The candidate list included "computation of ", "assignment", "multiplication", and "for loop". The professor rejected the last one dramatically — "don't give me this for loop, that breaks my heart":

Q: What is the basic operation here? The for loop?

A: No — never say "for loop". A loop only controls how many times other statements run; it is not an operation. The basic operation is the multiplication.

The full reasoning: the statement bundles a multiplication and an addition. Because the two can be written as two separate statements, we may pick either one — but the multiplication must be in the basic operation. "Computation of " is acceptable as an answer, but "multiplication" is the more specific one. A related question settled the addition-versus-multiplication choice:

Q: Can we take the whole statement, multiplication and addition together, as the basic operation?

A: You can, but now you are professionals. The two operations in C[i][j] = C[i][j] + A[i][k] × B[k][j] can be split into two statements, and since both take equal time, you pick either one — the multiplication is the safer choice.

Does the count depend on the worst case or the best case? No — whatever numbers are in the matrices, as long as they are square, the algorithm walks the entire structure. Every input of size triggers the same number of multiplications.

The count is the triple sum — one unit of constant time for the basic operation, per execution, over all three loops:

Each sum contributes a factor of , so expanding the summations gives times times : the basic operation executes (n cube) times. The professor noted you may name the counting function as you like — call it for the number of multiplications, say — but the usual convention is for the time taken. And here is the input size, not a loop variable.

Why three sums multiply to . The innermost loop executes times for each fixed pair: . The loop runs that inner block times: . The loop runs the whole thing times: . A nested loop multiplies the loop lengths — that is where the exponent comes from: three loops of length give .

2.5.3 Interpreting n³

The result is not just a number — it is a promise about how the algorithm scales. The interpretation the professor drilled:

  • When the size of the input is 2, the order of the time taken is .
  • When the size of the input is 3, the order of the time taken is .
  • When the input size is doubled, the time taken grows times.
  • When the input size is tripled, the time taken grows times.

Compare with ArrayMax: its time was , so doubling the input doubles the time (roughly). Here doubling the input multiplies the time by eight. That contrast — how the time responds to the input size — is the entire subject of the next section.

Worked example with real numbers. Multiply the matrices and , so .

  • Cell : : ; : . Sum: . Multiplications: 2.
  • Cell : : ; : . Sum: . Multiplications: 2.
  • Cell : : ; : . Sum: . Multiplications: 2.
  • Cell : : ; : . Sum: . Multiplications: 2.

Total multiplications: . The answer: 8 multiplications, and . Sense-check: 4 cells, each needing multiplications, gives — consistent with .

2.5.4 Your Turn: Element Uniqueness

The professor assigned the element uniqueness problem (check whether every element of an array is distinct) as a self-study exercise, with a hint that makes it easy: the provided algorithm has only one statement, so there is no other option — the comparison is forced to be the basic operation. The calculation then follows the same pattern as ArrayMax and matrix multiplication: identify the basic operation, count executions, simplify the summation.

Self-check solution sketch (attempt it before reading). Compare every pair with , one comparison per pair. The inner loop over runs from to , so for a fixed it contributes comparisons, and

This is — a quadratic function of . The leading term is , so the order of growth is (quadratic), not . Note the difference from matrix multiplication: two nested loops give , three nested loops give . The comparisons here run about half a million times for ().

Pitfall — counting loop-headers as operations. "The for loop" is not an operation; it only controls how many times other statements execute. In this example the temptation is to name the nested loop itself as the basic operation — reject it, exactly as the professor did with "don't give me this for loop". A second trap: forgetting the in "input size " is the matrix order, and confusing the square case () with the general shape rule ( times gives ).

Recap + bridge: matrix multiplication's basic operation is the multiplication inside the innermost loop, executed times. Three nested loops of length multiply to . The dramatic contrast with ArrayMax's — doubling the input multiplies time by 8 instead of 2 — is the entire motivation for the next topic: the order of growth.

Real-world connection: matrix multiplication is the inner engine of graphics pipelines (transformation matrices), neural network training (weight-matrix updates), and scientific simulation (solving linear systems). That is why an algorithm that beats — like Strassen's , or library routines tuned for but with tiny constants — is worth billions: doubling a problem's size costs 8× with but only about 7× with , and for that constant gap is real money in GPU-hours.

2.6 Order of Growth

We now step back and ask what the results and really tell us. The answer is the concept of order of growth, and it is the bridge between counting operations and comparing algorithms.

2.6.1 Definition

The order of growth of an algorithm is how the value of — the input size — affects the time complexity of the algorithm; how the time complexity changes with respect to . The professor's working definition: how the value of affects the time complexity of an algorithm is called the order of growth of an algorithm.

Intuition: think of the input size as the dial on a machine. Turning the dial from to — doubling the input — produces some response in the running time. The order of growth is the shape of that response: does the time double? quadruple? multiply by eight? That response shape is what the order of growth names — , , , and the rest. We do not care about the exact number of milliseconds at one specific ; we care how the time changes as the dial turns.

So when we say "the order of growth is ", we mean: when the input size is doubled, the time taken increases times; when the input size is made 4 times, the time taken increases times. The phrase "the time complexity is a function of the input size" is exactly this idea — the function says how the time responds as the input grows.

2.6.2 The Function Classes

The running time of an algorithm can fall into a small set of standard classes, and the course uses these terms only:

Order of growth Form Example meaning
Constant same time no matter the input
Logarithmic grows slowly as grows
Linear time proportional to input size
n log n linear times a log factor
Quadratic time grows like the square of
Cubic time grows like the cube of
Exponential time doubles with each added input element
Factorial even faster than exponential

"Logarithmic" means , where is the input size. "Linear" means . "" means — there is no other way to write it. "Quadratic" is , "cubic" is , "exponential" is — and do not confuse quadratic with exponential; exponential means raised to the power . Factorial, , was not on the professor's slide list but is a legitimate order of growth (more on that below).

Reading the table as a ladder. The classes form a growth ladder, from slowest to fastest growth: constant < logarithmic < linear < < quadratic < cubic < exponential < factorial . Each step up the ladder grows faster than the step below it — meaning that for large enough , a higher class always eventually beats (takes more time than) a lower one. Two functions in the same class, like and , stay within a constant factor of each other forever.

2.6.3 Comparing Orders of Growth

With the classes defined, the professor ran a sequence of mini-comparisons, each with a rule to extract:

vs . Two students wrote algorithms for the same problem: one whose time is , the other . Which is better? Answer: they are the same — both are quadratic. The reason: the highest-order term has the same power in both. , so the leading term is ; the other is exactly . Same highest power, same order of growth. The professor's generic rule: the highest-order term decides.

But the follow-up sharpened what "same" means:

Q: If n is less than 200 — say n = 1 or n = 4 — is n² + n really better than 200n²?

A: At n = 1, n² + n gives 2 and 200n² gives 200, so for small n the first algorithm wins. But we never judge by small numbers. There is a range after which the comparison holds, and we only talk about very large n. Both are quadratic, so they have the same order of growth.

The lesson: when we explain time complexity, there is a range after which the comparison becomes effective. We are not talking about small numbers — we are talking about very large. This is exactly the asymptotic behavior that motivates Section 2.7. "Asymptotic" means approaching a large value but never reaching it — assume it to be a very large one. The notations of the next section are defined for very large values of ; do not confuse the picture by plugging in 1 and 2.

vs . Which is larger? is larger in order of growth. At the assumptions break (100 vs 0.01), but do not go with small values of and confuse yourself. We are talking about order of growth: as the input size changes, grows slower than , so has the lower order of growth. Even though starts 10,000 times larger at , the cubic function eventually overtakes it — solve to find the crossover at , and for any beyond that the cubic is larger forever.

vs . Natural log vs base-2 log — same order of growth. The professor pointed to the change-of-base formula and told everyone to refresh logarithms and exponents, referring to textbook section 1.3.2. The derivation: . The denominator is a constant, and constants are ignored in comparisons, so

"When I say same order of growth, I am not talking about the values being equal. I am not even bothered about that." The two functions are different functions — and give different numbers — but they are the same class: one is a constant multiple of the other, and constant multiples are invisible at the order-of-growth level. This is why textbook complexity statements freely write without specifying the base.

vs . Same order of growth — two to the power minus one equals divided by 2:

and is a constant, so it is ignored — the two have the same order of growth. Exam note: if you can answer these four comparison questions (same-power polynomials, vs , log-base change, vs ), the order-of-growth topic is done.

Worked comparison: the two student algorithms with numbers. Algorithm A takes , algorithm B takes .

  • Expand A: , so .
  • At : , . A is slower — but this is the small- region we ignore.
  • At : , . A is still slightly slower — still a constant-factor gap.
  • At : , . The ratio approaches — a constant, forever.

Both are ; neither is asymptotically better. The verdict: same order of growth — both quadratic. Sense-check: the leading terms and differ only by a constant factor, which the comparison ignores.

2.6.4 Predicting Time When the Input Size Doubles

The most exam-relevant skill is translating an order of growth into a concrete time prediction. The professor ran three questions, all with the same setup — a program that takes 5 seconds for an input of size — and each time the input size is doubled.

Order of growth . When the input is , the time taken is proportional to . When the input is , the time is — four times the original. So the answer is seconds. The class produced a shower of wrong answers (25, 15, 10, 100) before the professor landed on 20; one student's "100" drew the deadpan response that if the answer were 100, the professor would leave the class. The lesson: doubling the input quadruples the time for a quadratic algorithm.

Order of growth . Linear: time is proportional to . Doubled input means double the time: seconds. "I don't want to see any other answer than 10 seconds."

Order of growth . Cubic: , so the time becomes seconds. "I don't want to see any other answer than 40 seconds."

The pattern: doubling the input multiplies the time by for linear, for quadratic, for cubic. Study advice: if any of this doubling logic did not land, sit down calmly after class and redo the calculations by hand — "you have to somehow get that. I will not explain it any better."

Worked example: all three doubling predictions, one setup. A program takes 5 seconds at input size . The input is doubled to . The answers are:

  • Linear : time , doubled input gives time → seconds. Answer: 10 seconds.
  • Quadratic : time , new time seconds. Answer: 20 seconds.
  • Cubic : time , new time seconds. Answer: 40 seconds.

Sense-check with the multiplier rule: gives 10, gives 20, gives 40 — the exponent of the order of growth is exactly the exponent in .

2.6.5 The Growth Table at Small Input Sizes

To make the classes tangible, the professor evaluated them at a small input size, (using base-2 logarithms):

Order of growth Value at
Constant 1
Logarithmic () 2
Linear () 4
8
Quadratic () 16
Cubic () 64
Exponential () 16

Already at such a small input the variation is visible — and the exponential class (16) sits below the cubic class (64). Why? Because the input size is small. We are not bothered by that: we find the value of after which the exponential class takes over, and only then do we say the comparison holds. There exists a threshold beyond which the ordering of the classes is the ordering you expect (constant < log < linear < < quadratic < cubic < exponential < factorial).

At , every polynomial class takes 1 unit (the log classes take 0, since ), and only the exponential class takes 2 — so even at the smallest input, the exponential algorithm is already slower than the constant one. The professor's point: even when the input size is 1, the exponential algorithm is taking more time.

One more practical warning came with the table: "There may even be some situations in which the constant is so huge in a linear algorithm that even an exponential algorithm with a small constant may be preferable in practice." A linear algorithm with an enormous constant can lose to an exponential algorithm with a tiny constant — for realistic input sizes. "But then, we are worried only about large values of ."

Visual intuition for the ladder. Plot time on the vertical axis and input size on the horizontal axis, for from 1 to, say, 100. The constant class is a flat horizontal line. The logarithmic class is a very shallow curve that rises quickly at first and then flattens out. The linear class is a straight diagonal line. Quadratic is a parabola bending upward; cubic bends upward even harder; exponential rises almost vertically and leaves the page almost immediately. The landmark to look for: the crossing points where a higher class overtakes a lower one (for example, where overtakes — around in this range). The one-sentence takeaway: the ordering of the curves is stable for large , and that stable ordering is exactly the class ladder.

2.6.6 Quick Order-Identification Exercise

The professor drilled the class with functions to classify by their order of growth. Each answer, with the reasoning:

Function Order of growth
Exponential (the factor 3 is a constant)
Linear
Constant — whether it is one or ten thousand or five thousand, it is constant
Linear (the factor is a constant)
Cubic
Exponential
Quadratic
Constant

A note on classification vocabulary: "cubic, quadratic, everything can be combined under polynomial" — polynomial is the umbrella term — "but be specific. If you are able to write it as specific, be specific. This is cubic."

A question from the class about the missing factorial class got a practical answer:

Q: Why is there no factorial in the list of orders of growth?

A: The textbook list does not include factorial. It is fine to write n! as an order of growth — no worries.

And the polynomial-vs-specific distinction, captured as a vocabulary correction:

Q: Can I just write "polynomial" for every power of n?

A: Cubic, quadratic, and the rest can all be combined under the umbrella term polynomial. But be specific: if it is cubic, write cubic. Specific terms carry more information.

2.6.7 Counting Nested Loops: Rules of Thumb and an Exam Warning

The quick way to estimate the order of growth of nested loops: if there are two loops each going from 0 to (or 1 to ), the time is ; if there are three loops, it is . The professor's first refinement: check whether the loops go from 1 to or from 0 to — the bound matters for exact counts, though not for the order.

The exam warning that followed is one of the most important lines in the whole session:

Exam note: When asked to compute the complexity of nested loops, do not write "there are three loops, so it is cubic." That answer gets cut, and it gets zero marks. You must give the reason: there is one basic operation that is executed this many times, so the time is or .

The example that completed the lesson had non-identical bounds: an loop going from 0 to , and inside it a loop going from down to 0. When is , the inner loop still comes down to 0, so the number of executions of the single basic operation (the summation) is

and the answer is — with the reason stated, not asserted from the number of loops.

Worked example: the descending inner loop. For each fixed , the loop runs from down to 0 inclusive — that is executions. So the total count is

Try : the pairs are , , , , , , , , , — that is executions, and . The leading term is , so the order of growth is , not — the loop count shrank from a full grid to half a grid. Sense-check: the sum of is , which is quadratic, so is right.

2.6.8 Why We Care: The Prime-Checking Example

To motivate all of this, the professor posed a question: why should we care about order of growth at all? The answer was a head-to-head comparison of two algorithms for the same problem — checking whether a given number is prime.

Algorithm 1 divides by every number from 2 to . There is nothing wrong with this algorithm as a procedure — but the moment you analyze its complexity, it loses. In the worst case (the number is prime, so every division must be tried), the number of division operations is . Assume one millisecond per division: for , the time taken is (units); for , it is .

Algorithm 2 divides only by the numbers from 2 to . In the worst case the loop runs times (call it , where is the input size). For : , so units. For : , so units.

Even at such a small input size, the difference is stark: 99 units vs 9 units. An efficient algorithm completes the job in 9 milliseconds while the other algorithm is still executing. That is the reason we study order of growth: the two algorithms have orders of growth (Algorithm 1) and (Algorithm 2), and the gap between and only widens as grows. Exam note: this is the canonical example of why complexity analysis beats "it works" — the faster algorithm wins the comparison on any realistic input.

Worked example: prime checking, end to end. Check whether is prime, at one millisecond per division.

  • Algorithm 1 (divide by every number from 2 to ): divisions = . Time = 99 ms.
  • Algorithm 2 (divide by every number from 2 to ): divisions = . Time = 9 ms.

Why is Algorithm 2 correct? If is composite, it has a factor with — if both factors exceeded , their product would exceed . So checking only up to misses nothing. The verdict: 9 ms beats 99 ms — over 10 times faster, even at a tiny input. The orders of growth are vs , and the gap only widens: for , Algorithm 1 needs about ms (16+ minutes) while Algorithm 2 needs about ms (1 second). Sense-check: , so the ratio — the gap is exactly the square root of the input.

2.6.9 A Peek at Best and Worst Cases

Right before moving to asymptotic notation, the professor teased the next topic with an example (the input-type dependence from Step 3 of the recipe). For an algorithm that scans an array for the maximum and keeps an assignment counter: in the best case, the very first element is already the maximum, and the time taken is . In the worst case, it is . The definitions previewed: the best case is where the algorithm takes the least time to execute; the worst case is where it takes the maximum time to execute. Full treatment comes in a later session.

Where do and come from? This is the classic arrayMax analysis counting every primitive operation (textbook Section 1.1). Setup: initialize (2 ops), initialize the loop counter (1 op), check the loop condition times, execute the loop body times, return the result (1 op). In the loop body: compare with (2 ops: indexing + comparing), possibly assign (2 ops: indexing + assigning), and increment the counter (2 ops). So the body costs 4 ops when no assignment happens and 6 ops when it does:

The best case occurs when is already the maximum — the assignment never fires. The worst case occurs when the array is sorted in increasing order — the assignment fires on every iteration. Both are linear functions, so both have the same order of growth ; the difference is only the constant (5 vs 7). This is the first taste of why "best case vs worst case" usually changes the constant, but only sometimes changes the order.

Recap + bridge: order of growth is how the time responds to the input size — the class ladder from constant to factorial. Comparisons ignore constants and lower-order terms; doubling the input multiplies time by . The same idea, made precise and symbolic — with the guarantee "for all large enough " — becomes asymptotic notation, the topic of Section 2.7.

Real-world connection: the prime-checking contrast is exactly why cryptographic systems choose the input sizes they do. Modern RSA keys are hundreds of digits long; the security argument depends on factoring and primality tasks being just hard enough — a -style improvement in primality testing is welcomed, while an unexpectedly fast factoring algorithm (an -order jump in the other direction) would force every certificate authority to lengthen its keys overnight. Order of growth is the language in which such security margins are debated.

2.7 Asymptotic Notation: The Informal Definitions

We already know what order of growth is — how the time taken by an algorithm grows with respect to . But knowing the orders is not enough; we still need a professional language to compare algorithms. That language is asymptotic notation.

2.7.1 Why We Need Asymptotic Notation

The precise meaning of the word "asymptotic" is the most important fact about it, and it is easy to ignore: how the running time of an algorithm increases with the input size, as the size of the input increases without bound. As approaches infinity — that is when we talk about order of growth and asymptotic notation.

Intuition: "asymptotic" is the "long run" of algorithm analysis. A sprinter may win the first 10 meters, but we judge the race by who wins the marathon. Asymptotic notation asks: when the input is huge — far beyond any size we could ever type in — which algorithm wins? The word carries the guarantee that we are looking far enough ahead that transient small- effects have settled down.

The motivating problem: I know algorithm 1 takes time and algorithm 2 takes time, but how do I say, professionally, that one is better than the other? I need some notation to write it down: the time taken by algorithm 1 is, say, big O of ; the time taken by algorithm 2 is big O of — and now the comparison is written down. Real-world: this is exactly the situation at work — tomorrow, you and a colleague are each asked to write an algorithm for the same problem; you submit both, and the manager has to choose. The one with the better order of growth gets the project. Asymptotic notations are the notations used to compare algorithms based on the order of growth of their basic operations, as the size of the input increases without bound.

The three main notations are big O, big omega (), and big theta (). Why the word "asymptotic"? Because the whole discussion assumes the input size grows without bound.

2.7.2 Big O: Lower-or-Same Order of Growth

Let be the order of growth of some algorithm. The informal definition: is the set of all functions with a lower or same order of growth as — to within a constant multiple, as goes to infinity. The two clauses in brackets are implicit: whenever you hear "asymptotic notation", assume "as goes to infinity" and "to within a constant multiple".

Membership works like a set. Suppose . Then:

  • — an algorithm with order of growth has a lower order of growth than , so it belongs.
  • — an algorithm with order of growth grows much faster than one with order ; its time grows faster, so it is outside the set.

In words: everything that grows no faster than lives inside .

2.7.3 Big Omega: Higher-or-Same Order of Growth

is the set of all functions with a higher or same order of growth as . The mirror image of big O. Following the same membership logic:

  • — the order of growth of is lower than the order of growth of .
  • — higher order of growth, so it belongs.
  • — same order of growth, so it belongs.

The key sentence that ties O and together: is an element of big O of and an element of big omega of , because big O takes lower-or-same growth and big omega takes higher-or-same growth.

2.7.4 Big Theta: Same Order of Growth

is the set of all functions with the same order of growth as . Theta means the order of growth matches exactly. If an algorithm has order of growth , then every algorithm with the same order of growth can be expressed as . For example, . This comparison applies to polynomials in — "we are talking about time complexity in the order of with respect to ."

The informal picture is complete: O is the upper family, the lower family, the exact match.

2.7.5 Student Questions

Two questions from the class sharpened the informal picture:

Q: Do 100n² and 0.1n³ take the same time for some value of n — say n = 100?

A: Yes, for particular values of n two different functions can coincide, and that is exactly why the formal definition carries the constants clause at the end — the c and the n₀ absorb such coincidences.

The professor accepted the student's claim, and the precise value of where the coincidence happens is easy to check: when , that is, at — where both functions equal . (At they do not coincide: while .) The lesson is not the exact value but the phenomenon: two different orders of growth can still take the same value at isolated points, so a claim of the form " grows no faster than " cannot be verified one point at a time. That is why the formal definition (Section 2.8) uses constants and a threshold: it says "for all large , up to a constant factor".

Q: Can a function be in big O of g(n) and also in big theta of g(n)?

A: Yes. Take n cubed: it is an element of O(n cubed) — lower or same order — an element of Ω(n cubed) — higher or same order — and an element of Θ(n cubed) — the same order. All three contain n cubed.

So membership in all three notations is possible; it happens exactly when the function has the same order of growth as .

Recap + bridge: informally, = lower-or-same growth, = higher-or-same growth, = exact match. A function with the same order as belongs to all three. The informal picture answers "what does it mean?" — the formal definitions in Section 2.8 answer "how do you prove it?"

Real-world connection: this set-based way of speaking ("") is how engineers state capacity contracts: "our service handles up to requests per second for servers" is a guarantee about growth — add servers and the capacity grows linearly — which is exactly the property a manager can budget against, ignoring constant factors like which cloud provider.

2.8 Big O: Formal Definition and Worked Proofs

The informal definition tells you what big O means; the formal definition tells you how to prove membership. This section builds the formal machinery and works two proofs in full, plus a graphical view.

2.8.1 The Formal Definition

Let and be functions from the non-negative integers to the non-negative reals. The domain-and-codomain clause matters — the time taken is always positive, so both sides are non-negative; "we never check this, assuming that the time taken will be always positive."

Definition. if and only if there exists a real constant and an integer constant such that

Three pieces deserve unpacking. First, the constant : the definition says "to within a constant multiple" — scales up or down. Second, the constant ("n zero"): the claim holds only from some threshold onward. Third, the graph picture: should be above — the line stays below the line , but only after . Before , we cannot predict which function is bigger; is the point after which the inequality is guaranteed.

Why constants and thresholds at all? The class asked exactly this, and the answer connects back to the small- warnings:

Q: Why do we need the constants c and n₀ at all?

A: Because the claim is not true for every input. For very small inputs, an exponential algorithm can beat a linear one — with inputs from 1 to 10 we might even prefer the exponential algorithm. In today's world inputs are large, so we find an n₀ and a constant after which the inequality is guaranteed to hold.

Two more clarifications that came up in the chat:

Q: When we compare two algorithms, do we consider the constant c?

A: No. We compare under the assumption that n is very large. For very large n the c constraint is always satisfied, so the constant drops out of the comparison.

Q: Are f(n) and g(n) for the same algorithm?

A: No. f(n) is the time complexity of one algorithm and g(n) is the time complexity of another algorithm. We are comparing two algorithms and deciding which one is better.

One remark on the formal definition in practice: only the highest-order term matters. When you write , you can put any constant in front of the highest term — "I can write ; it does not matter; only the highest term matters." A theorem in the textbook states this formally: constants and lower-order terms can be ignored in asymptotic comparisons.

2.8.2 The Table Method for Finding c and n₀

To prove you must produce a specific constant and a specific constant and then show the inequality holds for all . The goal is to find a pair and verify it. The professor's practical method for finding the pair is the table method:

  1. Randomly substitute values for (1, 10, 100 — convenient numbers).
  2. For each, compute .
  3. From , the ratio gives a candidate . Round the ratio up to a nice constant.
  4. Each row gives a valid pair — and the pairs must be used consistently: if you pick , you must use the from the row, not a from another row.

Proving any one valid pair is enough to prove big O. "You will find different methods to prove this... Stick to any one." Note that the ratio is computed because the definition rearranges to — that is where the constant comes from.

2.8.3 Worked Proof: 3n + 7 ∈ O(n)

Claim: is in , with . We know by looking that the highest term of is and 's highest term is — same order of growth, constants ignored — so it is in big O of . But the proof:

Step 1 — find candidate pairs. Substitute convenient values of and compute :

take
1 10 1 10 10
10 37 10 3.7 4
100 307 100 3.07 4

The three rows give three valid pairs: , or , or . Any one of them proves the claim. Do not mix: goes with , not .

Step 2 — prove the inequality for the chosen pair. Take . We must show

The professor's rule: start from the lowest-order term. Here is the lowest-order term, so we work on it first. Since , we have . Replace the in by the larger :

So for every , which is exactly with and . The proof is complete. The professor's commentary: "It is a very simple proof but a bit indirect way. First we have to understand what we are trying to do" — find and such that the definition's inequality holds — "and it might take a bit of time for you to digest this. But you will get it."

2.8.4 Worked Proof: n² + 2n + 1 ∈ O(n²)

Claim: — "n square plus two n plus one" — is in , with . Same two-step shape.

Step 1 — find candidate pairs.

take
1 4 1 4 4
10 121 100 1.21 2

Two valid pairs: or . Pick the pair that is comfortable to work with — the professor picked the first.

Step 2 — prove the inequality. With , we must show

Work on the lowest-order term repeatedly, converting it to the next-higher-order term each time. First, the constant : since , we have , so replacing by only grows the polynomial:

Now the lowest-order term is : since , we have , so

Chain the two steps: , so the inequality holds for all . Done.

The professor's reaction to the blank faces: "All of you are in blank. It would be easy to understand if you imagine graphically." The repeated move is always the same — take the lowest-order term, convert it to the next-higher-order term, repeat until the expression reaches the target .

The same question the class raised, and the professor's re-explanation:

Q: Why do the proofs always start from the lowest order term?

A: The lowest order term is what keeps us away from the target c·g(n). In 3n + 7, the 7 is the lowest order term, so we bound it: n ≥ 1 gives 7n ≥ 7, and 3n + 7 ≤ 3n + 7n = 10n. We convert the lowest order term to the next higher order term, step by step, until the expression becomes c·g(n).

Worked proof, fully annotated: with .

Goal: show for all .

  • Step A (eliminate the constant): for , so .
  • Simplify the right side: .
  • Step B (eliminate the linear term): for , so .
  • Simplify: .

Chained: , i.e., for every . Proved: . Sense-check: at , holds with equality; at , holds comfortably — the gap only widens as grows.

2.8.5 The Graphical View: 2n + 10 ∈ O(n)

The same ideas become visual with a plot. Claim: , with . Plot the blue line and the red line : the blue line stays above the red line everywhere, so we cannot claim — the constant is genuinely needed.

Compute a candidate : leads to a value of . Plot the green line . Now there is a crossing point — around — and for every beyond it, the green line sits above the blue line :

Before the crossing we cannot predict the behavior of the functions; after it we are sure. That crossing value is , and the constant is . This is precisely the "find a number after which the function holds true" idea, drawn instead of proved.

Visual intuition for the plot. Axes: horizontal axis is the input size (from 0 to about 30), vertical axis is the value of the function. Three straight lines: the red line (slope 1, the reference ), the blue line (slope 2, starting at height 10 — above the red line everywhere in view), and the green line (slope 3). Landmarks: the blue and green lines cross at — at exactly that point both equal 30. To the left of the crossing the blue line is above the green line; to the right the green line is above the blue line forever. The takeaway: the green line dominates the blue line after the threshold , which is the formal definition drawn as a picture.

A free online graphing tool, or a small Python plotting script, will reproduce these lines — the script is the kind of thing to keep for checking such graphs by hand.

2.8.6 Practice and the Theorems

The professor promised to post 10 to 12 practice functions for order-of-growth and big-O work. The study advice attached to them: try every problem yourself first, and only then check the solutions; solutions are hidden until someone attempts each problem — if nobody posts doubts, the assumption is that everyone understood. Exam note: proving the textbook's theorems (constants and lower-order terms can be ignored, and the other theorems in the chapter) will not be asked in the exam — the textbook covers them directly, so read them there.

Recap + bridge: big O is proved by producing one valid pair and verifying from onward — the table method finds the pair, the lowest-order-term conversion proves it, and the graph draws it. Exactly the same machinery, with the inequality flipped, proves big omega next.

Real-world connection: the table method mirrors how engineers sanity-check performance claims: pick a few representative sizes, compute the ratio of the two functions, and confirm it stays bounded. And the pair is literally the "service-level objective" of a performance guarantee — "after our warmup period , response time stays under (reference curve)" — the same two numbers, in a production monitoring dashboard.

2.9 Big Omega: Formal Definition and Worked Proof

If you understood the formal definition of big O, big omega is exactly the same concept — with the inequality flipped.

2.9.1 The Formal Definition

Definition. if and only if there exists a positive constant and a positive integer such that

Conceptually identical to big O: find a constant and a constant , and show the inequality holds from onward. The difference is the direction — now must sit above instead of below it.

2.9.2 Worked Proof: 3n + 7 ∈ Ω(n)

We already proved ; the same function is also in . With and , we need and such that for all .

The ratio flips: for big O we computed ; for big omega we compute . At , , which gives . The chosen pair: . The proof:

The move differs from the big O proof. In big O we converted the lowest-order term to the next-higher-order term; in big omega the proof simply drops the lower-order term — the only adds to the value, so throwing it away makes the left side smaller, and the inequality is immediate. Then for . so for all . Done.

Worked proof, fully annotated: with .

Goal: show for all .

  • Step A (drop the positive constant): since , dropping it only shrinks the left side: .
  • Step B (drop the factor): for , .
  • Chain: .

So for every . Proved: . Sense-check: dropping a positive constant and a factor bigger than 1 can only shrink the left side, so both inequalities are safe in the direction. This is why the omega proof is "easier" — the big-O proof had to build up terms to reach ; the omega proof only has to throw terms away.

The question that came up about the flipped ratio:

Q: For big O we computed f(n)/g(n). Why do we compute g(n)/f(n) for big omega?

A: The ratio flips because the inequality flips. Big O needs f(n) ≤ c·g(n), so c comes from f over g. Big omega needs f(n) ≥ c·g(n), so c comes from g over f.

The reasoning in detail: big O's requirement rearranges to — the constant must be at least the ratio of the two functions. Big omega's requirement rearranges to — or equivalently works from the other side, . The division flips precisely because the inequality flips; the two notations are mirror images in every mechanical step.

2.9.3 Big O Is an Upper Bound; Big Omega Is a Lower Bound

The two notations get their standard names from these pictures. Big O is called the asymptotic upper bound: collects all functions with a smaller or same order of growth as — everything that grows no faster — so it bounds the function from above. Big omega is called the asymptotic lower bound: collects all functions with a same or higher order of growth than — everything that grows no slower — so it bounds the function from below.

A consequence for analysis: once you have computed an order like , you do not even consider the part — a theorem states that constants and lower-order terms are ignored in these bounds. The bound is read from the highest-order term alone.

Recap + bridge: omega is O with the inequality reversed — — the proof drops lower-order terms instead of converting them, and it is called the asymptotic lower bound. When the upper bound (O) and the lower bound () meet at the same order, the notation that records that meeting is big theta — next.

Real-world connection: lower bounds are the language of impossibility and guarantees in industry — "no algorithm can sort faster than on comparison alone" tells a company that a claimed sorter cannot exist, saving months of wasted engineering. Upper bounds are achievable promises ("our product runs in "); lower bounds are physics ("you can never do better than ").

2.10 Big Theta: The Tight Sandwich

Big theta is the notation for an exact match — and the only one with a catch.

2.10.1 The Formal Definition

Definition. if and only if there exist positive constants and and a non-negative integer such that

Note the catch: this time there are two constants, and is sandwiched between them — must sit between and , for every from the threshold onward. Also note is now non-negative (the definition allows , whereas big O and big omega used ).

Because the sandwich forces to be within constant multiples of both from above and from below, is exactly the set of functions with the same order of growth as — matching the informal definition from Section 2.7.

Intuition for the sandwich: imagine two escalators running side by side in the same direction, one moving slightly faster than the other. If a person walking between them stays between the two escalators forever, their speed is "the same" as the escalators' speed, up to a constant. Theta says exactly that about functions: runs between (slower escalator) and (faster escalator) forever, so and are effectively the same growth rate. The two constants are the "slack" on each side; the threshold is the moment the escalators steady into their permanent order.

2.10.2 Worked Proof: 5n² ∈ Θ(n²)

The professor's remark: "Here we are not going to use that tabular method to show the proof. Because it is direct."

How do you prove — five n square in theta? Either prove and , or — simpler here — find a value of so that the left-hand side equals the right-hand side. Take :

Both sides equal , the middle is , so the sandwich holds with the same constant on both sides — the moment the two sides are equal, we may say the function is in . That is the direct proof: look at the function, find a constant making the sides match, and the theta membership follows. The general strategy for proving theta membership: show the function is both big O and big omega of the target, or find the sandwich constants directly.

Worked proof, fully annotated: .

Direct route — pick and :

  • Left inequality: . Both sides are , so is true for every — even without any threshold. Take (any works).
  • Right inequality: . Identical, true for every .

So holds for all . Proved: . Sense-check: the definition only needs between some constant multiples of — here both multiples collapse to the same value, which is the tightest possible sandwich. Alternative route (same verdict): with and with , and O plus Omega equals Theta.

Scope and pitfall. Theta demands both sides of the sandwich. A function like satisfies and — but because no can lift below forever. The classic beginner error is proving only one side and claiming theta. Also note the relaxed threshold: the definition allows (big O and big omega used ) — a small technicality, but exam questions sometimes check whether you noticed it.

Recap + bridge: theta is the tight sandwich — forever — the set of functions with exactly the same order of growth. Proof strategy: find the two sandwich constants directly, or prove O and Omega separately. Theta completes the "big" family; next come the strict versions, little o and little omega.

Real-world connection: theta is the notation of matching guarantees — a service that promises both "never faster than " and "never slower than " is telling you its exact growth behavior, which is what capacity planning needs: you can budget hardware knowing the true scaling law, not just an upper bound that might be loose by an order of magnitude.

2.11 Little o and Little Omega

Big O, big omega, and big theta are the notations we will always use. The little brothers — little o and little omega — differ from their big counterparts in exactly one character: the equals sign.

2.11.1 Strict Inequalities

The difference between big O and little o: equality is missing. Where allows to have the same order of growth as , requires to be strictly lower than in order of growth. The professor's test question: is an element of little o of ? No — the equality is missing, so a function with the same order of growth is excluded. But — big O keeps the equality.

Mirror image: little omega requires strictly greater. : yes, because has a strictly higher order of growth than a constant. In fact any function with growth strictly higher than constant qualifies — linear, quadratic, cubic, any of them: "constant time is the smallest order of growth, so I can have any function on the left-hand side which is greater than constant." So works too.

Intuition: think of big O as "≤" and little o as "<". Just as is true but is false, so is true but is false. The single character difference — the equals sign — is the whole story. Little o and little omega exist to express strictly faster/slower claims, when the equality case (same order) would overstate the relationship.

2.11.2 The Limit Shortcut

For proofs — which, the professor noted, will not be asked in the exam — there is an easy shortcut that avoids thinking about the definition:

Worked example: is in little o of ? As tends to infinity, the ratio tends to zero. Compute the ratio:

The lower-order terms do not matter — the ratio reduces to , and is 0, so the limit is 0. In words: twelve n square plus six n over n cube tends to zero, so twelve n square plus six n is little o of n cube. So . The same shortcut in the other direction proves little omega.

Worked example, fully annotated: .

Divide numerator and denominator by (the denominator's highest power), term by term:

Now take the limit as : and , so the ratio tends to .

By the shortcut, a limit of 0 means . Answer: . Sense-check: divided by is , which shrinks to zero — the numerator is genuinely one growth class below the denominator, so the strict little-o statement is right. (Contrast: over tends to 1, not 0 — which is exactly why .)

The professor's closing note on this topic: the limit trick is a shortcut for schoolkids — "you don't need this. You need the concept." Since big omega, big O, and big theta give you the concept, little o and little omega usually get skipped. Exam note: the limit shortcut is the fallback if a proof of little o or little omega ever appears — which, as stated, it will not.

Scope of the limit shortcut. The shortcut works when the limit exists and is or . When the ratio oscillates (no limit exists), the shortcut simply does not apply — you would need the formal definition instead. Also, the shortcut answers only the strict cases: a finite nonzero limit (say ) means the functions have the same order of growth — that is theta territory, not little-o territory. So read the cases carefully: for little o, for little omega, and any positive finite constant for same-growth (big theta).

Recap + bridge: little o = strictly lower growth, little omega = strictly higher growth — the same ideas as O and Omega with equality removed. The limit shortcut settles membership in one line: ratio → 0 gives little o, ratio → ∞ gives little omega. The "big" family is what the course actually uses; the choice between them is the next topic.

Real-world connection: strict inequalities appear in theory-driven engineering claims — "our streaming algorithm is per window" means it is genuinely sub-quadratic and will never degrade to quadratic, a stronger promise than ; system designers use such strict guarantees when proving that a component's cost cannot dominate the pipeline.

2.12 Choosing the Right Notation

Given a computed order of growth, which of O, , should you write? The professor's guidance is specific and exam-relevant.

2.12.1 Exact Results vs Slightly Higher or Lower

After calculating the time complexity of an algorithm, suppose the order of growth is exactly — just . Then all three notations are correct: big O of , big omega of , and big theta of . But the answer that earns full marks is theta of , because it is exactly — theta is the exact statement.

The moment the result has something more or less than , the choice matters:

  • Result (n cube plus 8), or : the order of growth is the same, but the function sits slightly above . Ideally, write — "this problem comes only when the power is same. Otherwise there is no confusion."
  • Result (n cube minus 100): the function sits slightly below . Write — not theta.

In practice, textbooks and websites write big O for almost everything, because they ignore the constants and the lower-order terms; the precise -vs--vs- choice matters only when the same highest power is involved, or when the exact notation is requested.

The class asked exactly how to make this decision, and the professor's answer is the master rule:

Q: After finding the order, what decides whether to write big O or big omega?

A: Look at the exact result. If you get n³ + 3, the function sits slightly above n³, so ideally write big omega of n³. If you get n³ − 100, it sits slightly below, so write big O of n³ — not theta. Textbooks mostly write big O either way, because they ignore the constants and the lower order terms.

And a related question about whether the bounds coincide:

Q: If the big O bound and the big omega bound come out the same, is that the same as big theta?

A: Yes. Theta means equality — the same order of growth. Big O is less than or equal and big omega is greater than or equal. When the equality symbol holds, all three coincide.

For , the analysis ignores the entirely — go with only , and conclude "cubic". That is the bound.

Why the power being the same is the only tricky case. If the highest power differs — say versus — the answer is unambiguous: is both not in and in . The choice problem appears only when the highest power is identical, because then constants and small offsets decide which side the function sits on. Written formally, for the same power :

The pushes the function above , so it is bounded from below by — omega. The pulls it below, so it is bounded from above — O. When the result is exactly , both bounds hold and the exact statement is theta.

2.12.2 Loose vs Tight Bounds: The Weight Analogy

When is a bound useful? The professor's analogy: the weight of a person. Suppose I say the weight of a 30-year-old person is upper bounded by 500 kg and lower bounded by 2 kg. Is that statement useful for guessing the weight? No — it is a very loose statement. Now give a tight upper bound of 60 kg and a tight lower bound of 50 kg: the weight is between fifty and sixty kilograms — now it is useful.

Map the analogy back: the lower bound is big omega, the upper bound is big O. Loose bounds — "at most 500 kg" — are technically true and practically useless. The rule: always go for the tight upper bound and the tight lower bound. That is what makes the notation informative — the professor's phrasing: "That lower bound is my big omega. And the upper bound of that 60 is my big O."

The weight analogy, made precise. A person's weight satisfies many true inequalities:

  • Loose: — true for every adult, and useless for guessing .
  • Tight: — still true (for this person), and now informative.

Translated: is the lower bound and O is the upper bound, and the tighter each one is, the more the pair pins down the true growth rate. A tight plus a tight pins the complexity at exactly . Loose bounds — "it is at most exponential" — are true but say almost nothing.

What is a bound at all? The class's answer: a bound is a limit. The professor's story to seal it — a piece of pedagogical humor worth keeping: in the epics, a circle was drawn around Sita telling her not to cross it — first credited to Rama, then corrected to Lakshman — "so from that time, bounds were there." From that story on, whenever you hear "bound", think "limit".

Pitfalls in choosing notation. (1) Writing when the function is : the function sits strictly above , so the exact-match claim is not the ideal answer — is. (2) Writing when the function is exactly : technically correct but not the full-marks answer — is exact and earns full marks. (3) Treating " is " and " is " as different facts when the result is exact — the exact result is theta, and theta implies both O and Omega.

Recap + bridge: exact result → write (full marks); result slightly above, like ; result slightly below, like . Bounds are limits — always report the tightest ones you can, because that is what makes a bound informative. With the notation settled, the last question of the session is about the algorithms themselves: when is an algorithm correct at all?

Real-world connection: choosing the right bound is the difference between a useful and a useless performance contract. "Our search runs in " is true but loose; "our search is " (or tighter) is what a buyer needs to plan hardware, price the service, and compare vendors. Reporting tight bounds — the 50–60 kg, not the 2–500 kg — is the professional habit the whole analogy is teaching.

2.13 Correctness of an Algorithm

The last topic of the session steps back from speed to ask a deeper question: when do we even say an algorithm is correct?

2.13.1 What It Means to Be Correct

An algorithm is said to be correct if, for every input instance, it halts with the correct output. Two failure modes make an algorithm incorrect:

  1. It does not halt on all input instances — there are inputs for which the algorithm never stops. This violates the finiteness property, so the algorithm is incorrect.
  2. It halts with an incorrect answer on some input.

A fair question follows: can we go ahead with an incorrect algorithm? The textbook's line — which the professor flagged as debatable — says an incorrect algorithm might still be useful if we can control the error rate and it can be implemented very fast. The professor was not sure about that statement.

In this course, the stance is firm: we will not go with any incorrect algorithm. In actual work, the choice is yours — but the theory is studied on correct algorithms only.

Why correctness has two independent conditions. "Halts" and "correct output" are separate promises, and an algorithm can fail either one. An infinite loop on some input fails the first condition but may produce perfect answers on the inputs where it does stop. A fast but wrong procedure (say, returning the second-largest instead of the largest) halts everywhere but fails the second. The definition requires both: for every input, the algorithm must stop, and what it prints must be the right answer. This is why the finiteness property from Section 2.1 is not a technicality — it is one half of the definition of correctness.

2.13.2 Industry Correctness: Google and the Intel Pentium Bug

In industry, correctness has a different flavor: an algorithm is accepted as correct when it delivers a tangible competitive advantage. If your company makes a profit from the algorithm, if it gives an advantage over your competitors, the company goes ahead with it and calls it correct.

Worked example: two industry verdicts on correctness.

Google search. Are you sure that when you search, you get the number one most fitting result? If you do not work at Google, you are not sure. But nobody cares — Google already "nailed Yahoo, Lycos, Alta Vista — everything into their box" — and it earns billions in pay-per-click revenue. How? Google delivers a good enough result quickly enough; we are happy with that, we do not check the proof that it is the best-fitting result, and the click-through rates are high enough to keep the paying customers paying. Verdict: accepted as correct — by the industry standard of competitive advantage.

The Intel Pentium bug. The Pentium chip's division failed only for a tiny sliver of results — a specific class of division problems around 0.0000001 (after the decimal point, six zeros, then a one) — and it failed only that many times. Even so, Intel had to call the chips back; the recall was forced because the chip was wrong in a specific, documentable way. The incident is widely documented as 1994. Verdict: recall-level disaster — the error was measurable and provable, so "good enough" did not apply.

The contrast is the lesson: the same company calculus — "does the error hurt the business?" — produced opposite verdicts. Google's ranking is unprovably "best" in a way nobody can even define precisely, so a good-enough result wins. The Pentium division error was a specific, documentable wrong answer on a hardware product that sold in the millions, so even an astronomically small error rate was unacceptable. The difference is not the size of the error; it is whether the error is measurable, correctable, and contractually load-bearing.

The exchange that framed the whole discussion:

Q: Can we go ahead with an incorrect algorithm?

A: The textbook notes it might be useful if we control the error rate and the algorithm runs very fast — though that claim is debatable. In industry, an algorithm is accepted as correct when it delivers a tangible competitive advantage: Google's search does not prove it returns the best result, but it returns a good enough result quickly enough, and that beats everyone.

So the two poles: industry accepts "good enough quickly enough" when it delivers competitive advantage (Google), while a measurable, correctable failure is still a recall-level disaster (Pentium). The course itself stays with the strict definition — correct means halts with the right output on every input — and the practical lesson is left to the working world.

Pitfall — confusing the course standard with the industry standard. In the exam and in this course, an algorithm is correct only under the strict definition: halts with the right output on every input. Do not answer an exam correctness question with the industry standard ("it is accepted because it is profitable") — that answer belongs to the discussion of this section, not to the definition. Conversely, do not go into industry believing every shipped system satisfies the strict definition — the Google example shows the working world runs on a looser, business-driven standard.

Recap + closing: an algorithm is correct if, for every input, it halts and produces the right output. The course studies only correct algorithms. Industry relaxes the standard to "good enough, fast enough, profitable" (Google) — until the failure becomes measurable and documentable (Pentium), at which point even a tiny error rate forces a recall. This closes the session: from the five properties, through counting, order of growth, and asymptotic notation, to the definition of correctness that makes analysis meaningful.

Real-world connection: the two poles of this section are literally the two sides of software engineering risk management. Recommendation and search systems ship with unproven optimality because their success metric (engagement, click-through, revenue) is business-defined — the "Google standard". Safety-critical systems — aircraft flight software, medical devices, financial settlement engines, chip arithmetic — must satisfy the strict standard, because their failures are measurable, provable, and catastrophic; the Pentium recall is the canonical case study taught in every hardware course for exactly this reason.

Exam Guidance Summary

Consolidated, everything the session said about exams and studying:

  • Properties of an algorithm: when asked, give the five specific terms — input, output, definiteness, finiteness, effectiveness. Generic answers like "efficient" or "unambiguous" do not get credit.
  • Basic operation questions: identify the single statement that executes the maximum number of times or takes the most time; never answer "the for loop" and never answer "the sum of all statements."
  • Nested-loop complexity: do not write "three loops, so cubic" — that answer gets cut and earns zero marks. State the reason: one basic operation is executed this many times, so the time is or . Check the loop bounds (0 to vs 1 to ).
  • Order-of-growth comparisons: be ready for mini-comparisons like vs (same — both quadratic), vs (the first has lower order), vs (same order — change of base; textbook section 1.3.2), and vs (same order).
  • Doubling-time questions: a program taking 5 seconds at input size takes 20 seconds (quadratic), 10 seconds (linear), 40 seconds (cubic) when the input doubles. If these did not click, redo them calmly by hand.
  • Classification: name the class specifically (constant, logarithmic, linear, , quadratic, cubic, exponential, factorial). "Polynomial" is accepted as an umbrella, but specific is better. is fine even though the textbook list omits it.
  • Choosing the notation: exact earns full marks with ; is ideally ; is ; textbooks default to big O.
  • Not asked in the exam: proving the textbook's theorems (constants and lower-order terms, and the other chapter theorems) — the textbook covers them; and proofs of little o / little omega — if one ever appears, use the limit shortcut.
  • Practice: 10 to 12 practice functions will be posted with solutions — attempt every one yourself before checking the solutions; go through the theorems in the textbook.
  • Course logistics: an assignment will be given (not sample papers) and reading it is enough to understand; the course is running behind, so expect a fast pace next session — keep up with the material.

Study plan distilled from the session. (1) Memorize the five property headings exactly. (2) Practice the five-step basic-operation recipe on ArrayMax, matrix multiplication, and element uniqueness until the summation step is automatic. (3) Drill the four order-of-growth comparisons and the doubling predictions (10/20/40 seconds). (4) Learn the formal definitions of O, , and one worked proof of each — the theorem proofs themselves are not asked, but the table method and the lowest-order-term conversion are the exam-relevant skills. (5) For nested-loop questions, always state the basic operation and its count — the reason, not the loop count.

Key Industry Applications

  • Real-world: hiring and project selection. When two people write algorithms for the same problem, the manager picks the one with the better order of growth — the entire reason asymptotic notation exists is to make that comparison professional and objective. Section 2.7's motivating story is not hypothetical: submitting two solutions with their complexity written down is the standard way candidates and teams defend their designs.
  • Real-world: Google search. Google's page-ranking algorithm for its search engine is the reason Google "nailed Yahoo, Lycos, Alta Vista": it delivers a good enough result quickly enough, generates billions in pay-per-click revenue, and keeps click-through rates high — the "good enough, fast enough" standard of industry correctness from Section 2.13.
  • Real-world: companies pre-decide algorithms. In established companies the algorithm is already chosen (Google has its page-ranking algorithm; the search engine is not going to change it), so the day-to-day work rarely involves re-justifying complexity. The need for this analysis appears when you design something new — which is exactly when the five-step recipe and the asymptotic comparisons of this session become the tools you use.
  • Real-world: Intel Pentium bug. A hardware division failure on a tiny class of inputs forced a chip recall (widely documented as 1994) — the cautionary tale that a small, controllable error rate can still be unacceptable, and that correctness recalls are business-level events. It is the counterweight to the Google standard: measurable, documentable errors are not forgiven by "good enough."
  • Real-world: plotting tools. Free online graphing tools and small Python plotting scripts make asymptotic comparisons (e.g., vs ) visually obvious — useful for checking your own proofs. Plotting the candidate lines and eyeballing the crossing point is the graphical twin of the table method.
  • Real-world: bottleneck analysis. The slowest-statement rule of Section 2.4 is the principle behind production-line and web-request optimization: find the bottleneck component and fix it — speeding up the fast parts changes nothing. The basic operation is the algorithmic name for the bottleneck.
  • Real-world: matrix multiplication at scale. The count of Section 2.5 is why graphics, neural-network training, and scientific simulation spend so much engineering on matrix libraries — doubling problem size costs 8× time, so constant-factor and exponent improvements in matrix multiply are worth real money.
  • Real-world: performance contracts. Tight bounds (Section 2.12) are how vendors state guarantees: "handles requests per second for servers" or "search is " — the loose 2-to-500-kg style contract is technically true and commercially useless.

DSA Lecture 2 notes · Algorithm Analysis: From Basic Operations to Asymptotic Notation

Data Structures and Algorithms· postgraduate· 2026-08-09

Sections Breakdown

12.1 The Five Properties of an Algorithm (Recap)

Recap of the five consolidated properties of an algorithm — input, output, definiteness, finiteness, effectiveness — with the live 'efficient vs effectiveness' vocabulary correction, Euclid's algorithm, experimental analysis, and the RAM model.

22.2 The Basic Operation Method

The basic operation as the statement that contributes the most to running time, the five-step recipe for non-recursive algorithms, and the counting formula C(n) = sum of 1 over the loop bounds.

32.3 Worked Example: ArrayMax

Why the comparison — not the guarded assignment — is ArrayMax's basic operation, the count T(n) = n − 1, and why counting the increment leaves the order unchanged.

42.4 Worked Example: Statements with Different Timings

Choosing the single slowest statement as the basic operation, with the parallel-execution intuition and why 'the sum of all statements' is an exam zero.

52.5 Worked Example: Matrix Multiplication

The triple-loop count T(n) = n³, the square-case shape rule, interpreting what n³ means, and the element-uniqueness self-study exercise.

62.6 Order of Growth

How the input size n affects time complexity: the function-class ladder, order comparisons, doubling-time predictions, the growth table, and the prime-checking motivation.

72.7 Asymptotic Notation: The Informal Definitions

Big O as lower-or-same order of growth, big Omega as higher-or-same, big Theta as exact match — with the student questions that sharpened the picture.

82.8 Big O: Formal Definition and Worked Proofs

The formal definition with constants c and n₀, the table method for finding pairs, and the worked proofs 3n + 7 ∈ O(n) and n² + 2n + 1 ∈ O(n²).

92.9 Big Omega: Formal Definition and Worked Proof

Big Omega as the flipped inequality, the dropping-term proof of 3n + 7 ∈ Ω(n), and big O versus big Omega as upper and lower bounds.

102.10 Big Theta: The Tight Sandwich

The two-constant sandwich definition, the direct proof of 5n² ∈ Θ(n²), and why proving only one side is not enough.

112.11 Little o and Little Omega

Strict inequalities — the missing equals sign — and the limit shortcut, with the worked example 12n² + 6n ∈ o(n³).

122.12 Choosing the Right Notation

When to write Θ, Ω, or O for exact, slightly-above, and slightly-below results — and the tight-bounds weight analogy.

132.13 Correctness of an Algorithm

The strict definition of correctness — halts with the right output on every input — and the industry standard of competitive advantage: Google and the Intel Pentium bug.

14Exam Guidance Summary

The consolidated exam guidance of the session: exact property headings, basic-operation reasoning, doubling predictions, and notation choice.

15Key Industry Applications

Real-world connections: hiring and project selection, Google search, the Pentium recall, bottleneck analysis, matrix libraries, and performance contracts.

Postgraduate students learning algorithm analysis and asymptotic notation

Exam Revision Notes

Below is the distilled, exam-ready core. Every entry comes from the full explanation above. Use this section for rapid review; return to the main notes when a point needs more context.

The Five Properties of an Algorithm (Recap)

Must-know: The five properties of an algorithm are input, output, definiteness, finiteness, effectiveness — give these exact headings, never generic words like 'efficient'.

Top pitfall: Answering 'efficient' or 'unambiguous' instead of the five consolidated headings; mixing effectiveness (executable in principle) with efficiency (fast).

Self-check: Name the five properties of an algorithm in the course's exact vocabulary.

Connects to: 2.2

The Basic Operation Method

Must-know: Basic operation = the statement that contributes the most to running time (executes the maximum number of times). Five-step recipe: input size n, basic operation, dependency check (worst/average/best), summation, simplification.

Top pitfall: Confusing basic operation with primitive operation; forgetting step 3 (check whether the count depends only on n), which forces worst/average/best-case splits.

Self-check: Why is the count written as a summation of ones?

Connects to: 2.3, 2.6

Worked Example: ArrayMax

Must-know: In a loop with a conditional update, the comparison is the basic operation because it executes every iteration; the assignment may be skipped. ArrayMax: T(n) = n - 1.

Top pitfall: Choosing the assignment inside an if-statement as the basic operation — it does not execute when the comparison is false.

Self-check: Why is the comparison the basic operation of ArrayMax and not the assignment?

Connects to: 2.2, 2.5

Worked Example: Statements with Different Timings

Must-know: The basic operation is the single statement that takes the most time — never the sum of all statements. Machine details (cores, language) are ignored in algorithm analysis.

Top pitfall: Answering 'the sum of all statements' or naming the second-slowest statement; both are exam zeros.

Self-check: In the five-statement example, why does statement 3 (0.25 ms) not decide the running time?

Connects to: 2.2, 2.3

Worked Example: Matrix Multiplication

Must-know: In matrix multiplication the basic operation is the multiplication (split from the addition), executed n^3 times via the triple sum; a loop is never the basic operation.

Top pitfall: Answering 'the for loop' or forgetting the square-case shape rule (n x n times n x n gives n x n).

Self-check: Why does the triple summation evaluate to n^3 and not 3n?

Connects to: 2.2, 2.3, 2.6

Order of Growth

Must-know: Order of growth = how n affects time complexity. Doubling input multiplies time by 2^exponent: linear 10s, quadratic 20s, cubic 40s for a 5s baseline. Constants and lower-order terms are ignored in comparisons.

Top pitfall: Writing 'three loops, so cubic' without stating the basic operation and its count — the answer gets cut for zero marks; judging order by small values of n.

Self-check: A program takes 5 seconds at size n. The input doubles. What does it take if the order of growth is n^3?

Connects to: 2.3, 2.5, 2.7, 2.8

Asymptotic Notation: The Informal Definitions

Must-know: Big O = lower-or-same growth (everything that grows no faster); big Omega = higher-or-same; big Theta = exact match. Same order as g means membership in all three.

Top pitfall: Testing asymptotic claims at one small value of n (e.g., n = 100) — isolated coincidences like 100n^2 = 0.1n^3 at n = 1000 do not decide growth order.

Self-check: Is n^4 an element of Omega(n^3)? Of O(n^3)?

Connects to: 2.6, 2.8

Big O: Formal Definition and Worked Proofs

Must-know: To prove f(n) in O(g(n)): find one valid pair (n0, c) and show f(n) <= c*g(n) for all n >= n0. Table method for the pair; lowest-order-term conversion for the proof.

Top pitfall: Mixing pairs — using n0 from one table row with c from another; the pair must be used consistently.

Self-check: Prove that 3n + 7 is in O(n) by stating a valid (n0, c) pair and showing the inequality.

Connects to: 2.7, 2.9

Big Omega: Formal Definition and Worked Proof

Must-know: Omega = lower bound: find c > 0 and n0 >= 1 with f(n) >= c*g(n) for all n >= n0. Proof style: drop positive lower-order terms; the ratio flips to g/f.

Top pitfall: Using the f/g ratio for omega — the ratio flips to g/f because the inequality flips.

Self-check: Why does the big omega proof drop the +7 instead of converting it to a higher-order term?

Connects to: 2.8, 2.10

Big Theta: The Tight Sandwich

Must-know: Theta = tight sandwich: c1*g(n) <= f(n) <= c2*g(n) for all n >= n0. Prove by finding both constants (direct) or by proving O and Omega separately.

Top pitfall: Proving only one side (O or Omega) and claiming theta — the sandwich needs both sides; n in O(n^2) does not make n in Theta(n^2).

Self-check: Why is n not an element of Theta(n^2) even though n is in O(n^2)?

Connects to: 2.7, 2.8, 2.9

Little o and Little Omega

Must-know: Little o = strictly lower growth (no equality); little omega = strictly greater. Limit shortcut: ratio tends to 0 -> little o; tends to infinity -> little omega. Proofs will not be asked in the exam.

Top pitfall: Claiming n^3 in o(n^3) — the same order is excluded because equality is missing; the limit is 1, not 0.

Self-check: Use the limit shortcut to decide whether 12n^2 + 6n is in o(n^3).

Connects to: 2.8, 2.9, 2.10

Choosing the Right Notation

Must-know: Exact n^3 -> Theta(n^3) (full marks); n^3 + 8 -> Omega(n^3); n^3 - 100 -> O(n^3). Bounds are limits; always report tight bounds.

Top pitfall: Writing Theta for n^3 + 8 (should be Omega) or O for an exact n^3 (Theta is the full-marks answer).

Self-check: You computed exactly n^3 + 8. Which notation should you ideally write, and why?

Connects to: 2.7, 2.8, 2.9, 2.10

Correctness of an Algorithm

Must-know: Correct algorithm = halts with the correct output for every input. Course standard is strict; industry accepts good-enough-fast-enough when it delivers competitive advantage (Google), but measurable errors force recalls (Intel Pentium).

Top pitfall: Answering an exam correctness question with the industry standard ('accepted because profitable') instead of the strict definition.

Self-check: What are the two failure modes that make an algorithm incorrect?

Connects to: 2.1, 2.6

Exam Guidance Summary

Must-know: Exam staples: five properties (input, output, definiteness, finiteness, effectiveness); basic operation is never 'the for loop' or 'the sum'; nested loops need the reason not the loop count; doubling predictions 10/20/40; exact n^3 -> Theta, n^3+8 -> Omega, n^3-100 -> O.

Top pitfall: Writing 'three loops, so cubic' without stating the basic operation and its count — cut for zero marks.

Self-check: Which two things are explicitly stated as not asked in the exam?

Connects to: 2.1, 2.2, 2.6, 2.8, 2.11, 2.12

Key Industry Applications

Must-know: Industry correctness is 'tangible competitive advantage' (Google accepted, Pentium recalled); the slowest statement is the bottleneck; complexity analysis matters most when designing new algorithms.

Top pitfall: Confusing the industry correctness standard with the course's strict definition.

Self-check: Why was Google's unproven ranking accepted as correct while Intel's tiny division error forced a recall?

Connects to: 2.4, 2.5, 2.7, 2.12, 2.13

Was this lecture useful?

Loading comments…
🤖

BitsNotes AI Assistant

Subject Notes Assistant

Configure AI Chat

Choose how to access the chatbot
Have your own API key?

Switch to "Bring Your Own Key" tab above for unlimited access with any OpenAI-compatible provider.

🔑 Enter API key above to fetch live models from provider, or enter model name manually.
OpenAI-Compatible API Support

Choose any provider preset (Gemini, DeepSeek, Kimi, GLM, MiniMax, Qwen, OpenAI, Groq, Ollama, etc.) or enter a custom endpoint URL.

Security & Privacy First

Your API key is sent directly from your browser to your specified provider. BitsNotes servers never store or see your key.