Introduction: Why Some Programs Are Faster Than Others

Have you ever wondered why your phone sometimes takes ages to search through thousands of photos, but a Google search across the entire internet takes less than a second? The secret isn't just a faster processor—it’s about Algorithm Efficiency.

In this chapter, we will learn how to measure how "fast" an algorithm is using a special mathematical language called Big-O Notation. Don't worry if you aren't a math expert; Big-O is simply a way to describe how much more work a computer has to do as we give it more data to handle.

Note: This chapter focuses on Time Complexity (how long it takes). Per the syllabus, you do not need to worry about Space Complexity (how much memory it uses) for your examinations.

1. What is Algorithm Efficiency?

When we talk about efficiency in Computing, we are asking: "As the size of the input (\(n\)) grows, how much longer does the algorithm take to finish?"

We don't measure this in seconds or minutes. Why? Because a powerful gaming PC will always run a program faster than an old laptop, even if the algorithm is bad! Instead, we measure efficiency by counting the number of basic operations (like comparisons or assignments) the computer has to perform.

The "Worst Case" Scenario

In the H2 Computing syllabus, we always look at the worst-case complexity. This means we assume the most "unlucky" situation possible. For example, if you are searching for a name in a list, the worst case is that the name is at the very end of the list or not there at all.

Key Takeaway: Efficiency is about the growth rate of an algorithm's execution time as the input size (\(n\)) increases.

2. Understanding Big-O Notation

Big-O Notation is a shorthand used to describe the upper bound of an algorithm's running time. It tells us the "order of magnitude" of the work required.

Imagine you are organizing a library. Let \(n\) be the number of books:

Constant Time: \(O(1)\)

The time taken is the same regardless of how many books there are.
Example: Picking up the very first book on the shelf. It takes the same time whether the library has 10 books or 10 million books.

Logarithmic Time: \(O(\log n)\)

The time taken increases very slowly as \(n\) grows. This usually happens when you "divide and conquer" (splitting the problem in half every step).
Example: Finding a word in a dictionary by opening it in the middle, then the middle of the remaining half, and so on (Binary Search).

Linear Time: \(O(n)\)

The time taken grows at the same rate as the input. If you double the books, you double the time.
Example: Reading every book in the library one by one from start to finish (Linear Search).

Linearithmic Time: \(O(n \log n)\)

This is slightly slower than linear but still very efficient for large datasets.
Example: Most efficient sorting algorithms, like Merge Sort or Quicksort.

Quadratic Time: \(O(n^2)\)

The time taken is the square of the input size. If you have 10 times more books, it takes 100 times longer! This usually happens when you have a "loop inside a loop."
Example: Comparing every book to every other book (Bubble Sort or Insertion Sort).

3. Comparing Searching and Sorting Algorithms

The syllabus requires you to know the worst-case efficiencies of specific algorithms. Use this table as your ultimate "cheat sheet" for Paper 1:

Algorithm Category Algorithm Name Big-O (Worst Case)
Searching Linear Search \(O(n)\)
Binary Search \(O(\log n)\)
Sorting Bubble Sort \(O(n^2)\)
Insertion Sort \(O(n^2)\)
Merge Sort \(O(n \log n)\)
Quicksort \(O(n^2)\)*

*Note on Quicksort: Although its "average" case is \(O(n \log n)\), its worst case is actually \(O(n^2)\) (e.g., when the pivot is always the smallest or largest element). Always use \(O(n^2)\) if the exam specifically asks for Quicksort's worst case!

4. Quick Identification Tips

If you are looking at pseudocode or Python code and need to guess the Big-O, look for these patterns:

  • A single loop that goes through the list once? Usually \(O(n)\).
  • A loop inside a loop (nested loops)? Usually \(O(n^2)\).
  • A loop where the range is halved every time (like low = mid + 1)? Usually \(O(\log n)\).
  • No loops at all (just simple math or variable assignment)? Usually \(O(1)\).

Did you know? \(O(n \log n)\) is the "Gold Standard" for sorting. While \(O(n^2)\) algorithms are easy to write, they become unusable for millions of data points because they would take years to finish!

5. Common Mistakes to Avoid

  • Mistake: Thinking \(O(n^2)\) is faster than \(O(n)\).
    Correction: In Big-O, a higher exponent or power is always slower for large amounts of data.
  • Mistake: Forgetting that Binary Search requires the list to be sorted first.
    Correction: While Binary Search is \(O(\log n)\), the act of sorting the list first might take \(O(n \log n)\).
  • Mistake: Including smaller terms.
    Correction: In Big-O, we only keep the "fastest-growing" term. If an algorithm takes \(n^2 + 5n + 10\) steps, we just say it is \(O(n^2)\).

Chapter Summary

1. Efficiency is measured by how time scales with input size \(n\).
2. Big-O Notation describes the worst-case scenario.
3. Searching: Binary Search \(O(\log n)\) is much faster than Linear Search \(O(n)\).
4. Sorting: Merge Sort \(O(n \log n)\) is generally more efficient than Bubble or Insertion Sort \(O(n^2)\).
5. Worst Case: Always assume the algorithm has to do the maximum possible work.

To see these algorithms in action, cross-reference this with the "Searching and Sorting Algorithms" chapter where we implement them in Python!