Algorithm design and problem-solving
1. Analysis Stage: This is where you define the problem. You must identify:
- Inputs: What data goes in?
- Outputs: What results are produced?
- Processes: How do inputs become outputs?
To manage complexity, Cambridge uses two key techniques:
- Abstraction: Hiding irrelevant details to focus on the core logic. For example, when designing a map app, you abstract away the color of the cars and focus only on road connections.
- Decomposition: Breaking a large problem into smaller, manageable sub-problems (e.g., splitting 'Calculate Payroll' into 'Input Hours', 'Calculate Tax', 'Output Net Pay').
2. Design Stage: You create the solution using:
- Flowcharts: Visual diagrams using standard symbols (Oval=Start/End, Parallelogram=I/O, Rectangle=Process, Diamond=Decision).
- Pseudocode: Text-based algorithm description.
- Structure Diagrams: Hierarchical breakdown of modules.
3. Coding & Testing: You write the code and test it using:
- Normal Data: Valid input (e.g., age 25).
- Boundary/Extreme Data: Values at the limits (e.g., age 0 or 150).
- Erroneous/Abnormal Data: Invalid input (e.g., age -5 or 'abc') to check error handling.
| Standard Pseudocode Syntax |
|---|
INTEGER countSTRING nameREAL average |
count <- 0(Use <- or = depending on syllabus version, but <- is safer for clarity) |
INPUT age |
PRINT "Result: ", result |
IF condition THEN<br> action<br>ELSE<br> action<br>ENDIF |
WHILE condition DO<br> action<br>ENDWHILE |
FOR i <- 1 TO n DO<br> action<br>NEXT i |
totalScore instead of x). Indentation is crucial for readability and showing block structure.// 1. Declare variables
INTEGER count
REAL number, total, average
// 2. Initialize total
total <- 0
// 3. Loop to get input
FOR count <- 1 TO 5 DO
INPUT number
total <- total + number
NEXT count
// 4. Calculate average
average <- total / 5
// 5. Output result
PRINT "The average is: ", average
Why this works:
- We use a
FORloop because the number of iterations (5) is known. totalaccumulates the sum. If we didn't initialize it to 0, it might contain garbage data.- Division by 5 is constant, so we don't need a variable for the count of numbers.
The Correction: This is Decomposition.
- Abstraction is about ignoring details (hiding complexity).
- Decomposition is about splitting the problem into modules or steps.
Examiner Tip: When asked to explain how abstraction helps, say: 'It allows the programmer to focus on the essential features of the problem without being distracted by irrelevant details.' Do not mention breaking things down.
Why Examiners Accept This: They want to see that you understand the logic, not just the syntax. Vague answers like 'It does math' get zero marks.
Correct Approach: Identify the specific operations:
- Does it find a maximum/minimum?
- Does it sort data?
- Does it calculate a total/average?
- Does it validate input?
Example Phrase: 'This algorithm iterates through a list of numbers, comparing each value to a stored maximum. If the current value is greater than the stored maximum, it updates the maximum. Finally, it outputs the largest number in the set.'
Key Markscheme Keywords: 'finds the largest/smallest', 'calculates total/average', 'validates input range'.
Given the following pseudocode, complete the trace table for inputs 10, 20, and 30.
total <- 0
FOR i <- 1 TO 3 DO
INPUT num
total <- total + num
NEXT i
PRINT total
Trace Table:
| i | num (Input) | total |
|---|---|---|
| 1 | 10 | 10 |
| 2 | 20 | 30 |
| 3 | 30 | 60 |
Final Output: 60
Explanation:
- Iteration 1:
i=1. Inputnum=10.totalbecomes0+10=10. - Iteration 2:
i=2. Inputnum=20.totalbecomes10+20=30. - Iteration 3:
i=3. Inputnum=30.totalbecomes30+30=60. - Loop ends. Print
total.
Purpose: To find a specific value (target) in an unsorted list.
How it works: It checks every element one by one from the start until the target is found or the end of the list is reached.
Pseudocode Structure:
found <- FALSE
index <- 0
WHILE (index < length OF list) AND (found = FALSE) DO
IF list[index] = target THEN
found <- TRUE
PRINT "Found at index", index
ELSE
index <- index + 1
ENDIF
ENDWHILE
IF found = FALSE THEN
PRINT "Not found"
ENDIF
Prerequisite: The list MUST be sorted (ascending or descending).
Purpose: To find a target value in a sorted list much faster than linear search.
How it works:
- Compare the target with the middle element of the list.
- If they match, return the index.
- If the target is less than the middle element, ignore the right half and search the left half.
- If the target is greater than the middle element, ignore the left half and search the right half.
- Repeat until found or list is empty.
[10, 20, 30, 40, 50, 60, 70] (Indices 0 to 6)Target:
42
| Step | Low Index | High Index | Mid Index (Low+High)//2 | Mid Value | Action |
|---|---|---|---|---|---|
| 1 | 0 | 6 | 3 | 40 | Target (42) > 40. Search right half. Low = 3+1 = 4. |
| 2 | 4 | 6 | 5 | 60 | Target (42) < 60. Search left half. High = 5-1 = 4. |
| 3 | 4 | 4 | 4 | 50 | Target (42) < 50. Search left half. High = 4-1 = 3. |
| 4 | 4 | 3 | - | - | Low > High. Loop ends. Not found. |
Note: Indices are 0-based in this example. Always check if the question specifies 0-based or 1-based indexing.
The Correction: Binary Search relies on the order of elements to eliminate half the list. If the list is unsorted, eliminating the 'right' or 'left' half might discard the target value.
Examiner Tip: If asked 'Why can't we use binary search here?', answer: 'Because the data is not sorted.' Do not say 'because it is too slow'—binary search is actually faster than linear search if the list were sorted.
Why Examiners Accept This: They look for the specific complexity class and the reason for it.
Correct Phrase: 'Bubble Sort has a time complexity of O(n^2) in the worst case. This is because it uses nested loops: for each element, it compares it with every other adjacent element. As the number of items (n) increases, the number of comparisons increases quadratically.'
Key Markscheme Keywords: 'nested loops', 'quadratic time complexity', 'O(n^2)', 'compares adjacent elements'.
Purpose: To sort a list of values in ascending (or descending) order.
How it works:
- Compare adjacent elements.
- If they are in the wrong order, swap them.
- Repeat this process for the entire list until no swaps are needed.
Pseudocode Structure:
FOR i <- 0 TO length OF list - 2 DO
swapped <- FALSE
FOR j <- 0 TO length OF list - 2 - i DO
IF list[j] > list[j+1] THEN
// Swap
temp <- list[j]
list[j] <- list[j+1]
list[j+1] <- temp
swapped <- TRUE
ENDIF
NEXT j
IF swapped = FALSE THEN
EXIT FOR // List is already sorted
ENDIF
NEXT i
i each time because the largest elements 'bubble up' to the end and don't need to be checked again. The swapped flag allows early exit if the list is already sorted (best case O(n)).[5, 2, 8, 1] being sorted. In Pass 1, the list becomes [2, 5, 1, 8]. Identify the sorting algorithm and explain why.Algorithm: Bubble Sort.
Explanation:
- The largest value (8) has moved to the end of the list in the first pass.
- This 'bubbling up' of the largest element is characteristic of Bubble Sort.
- In a Selection Sort, the smallest value would move to the front. In an Insertion Sort, elements are inserted into their correct position one by one, not necessarily moving the max to the end in the first pass.
Definition: Big O notation describes how the runtime or space requirements of an algorithm grow as the input size (n) increases.
Common Complexities:
- O(1): Constant time. Accessing an array index. Fastest.
- O(\log n): Logarithmic time. Binary Search. Very efficient for large data.
- O(n): Linear time. Linear Search. Time grows directly with input size.
- O(n^2): Quadratic time. Bubble Sort, Selection Sort. Time grows with the square of input size. Inefficient for large n.
The Correction:
- Oval/Circle: Terminal (Start/End).
- Parallelogram: Input/Output. Always use this for PRINT and INPUT.
- Rectangle: Process (Calculation, Assignment).
- Diamond: Decision (IF/ELSE conditions).
Examiner Tip: If you draw a rectangle for PRINT, you will lose marks. Examiners look specifically for the parallelogram shape for I/O operations.
Why Examiners Accept This: Maintenance refers to future updates. Code that is hard to read is hard to fix.
Correct Phrases:
- 'Use meaningful variable names (e.g.,
totalScoreinstead oft) so other programmers can understand the code without comments.' - 'Add comments to explain complex logic blocks.'
- 'Use modular design (subroutines/functions) to break the problem into smaller, testable parts.'
Key Markscheme Keywords: 'readability', 'meaningful identifiers', 'modular', 'comments', 'documentation'.
"abc" (Length 3)Purpose: To check that the program rejects erroneous/abnormal data that is too short.
Test Data 2: "abcdefgh" (Length 8)
Purpose: To check boundary/extreme data. This is the minimum valid length, ensuring the program accepts the lower limit correctly.