Testing algorithm ideas on small systems before formal complexity notation.
Lab Focus Run controlled experiments, collect evidence, and compare algorithm choices under explicit assumptions.
Part III introduced algorithm thinking:
Now we run controlled experiments.
The goal is not to chase micro-optimizations. The goal is to build engineering intuition by observing how different algorithm choices behave on the same task.
By the end of this lab, you should be able to:
Use one task, multiple strategies:
“Find a target value in a sorted sequence.”
Why this task:
Strategies:
We will track an explicit step counter to observe behavior directly.
Suggested file names:
algorithm_lab_1.nexalgorithm_lab_1_main.nexclass Search_Result
create
make(found: Boolean, index: Integer, steps: Integer) do
this.found := found
this.index := index
this.steps := steps
end
feature
found: Boolean
index: Integer
steps: Integer
invariant
index_valid: index >= -1
steps_non_negative: steps >= 0
found_matches_index: found = (index >= 0)
end
class Linear_Search_Algo
feature
find(items: Array[Integer], target: Integer): Search_Result
do
let idx: Integer := -1
let steps: Integer := 0
from let i := 0
until i >= items.length or idx >= 0 do
steps := steps + 1
if items.get(i) = target then
idx := i
end
i := i + 1
end
result := create Search_Result.make(idx >= 0, idx, steps)
ensure
hit_is_correct: not result.found
or items.get(result.index) = target
bounded_steps: result.steps <= items.length
miss_checks_every_item: result.found
or result.steps = items.length
end
end
class Binary_Search_Algo
feature
is_sorted(items: Array[Integer]): Boolean do
result := true
from let i := 1 until i >= items.length do
if items.get(i - 1) > items.get(i) then
result := false
end
i := i + 1
end
end
-- How many times n can be halved before nothing is left:
-- the most probes a binary search over n items needs.
max_probes(n: Integer): Integer do
result := 0
from let k := n until k = 0 do
result := result + 1
k := k / 2
end
end
find(items: Array[Integer], target: Integer): Search_Result
require
sorted_input: is_sorted(items)
do
let lo: Integer := 0
let hi: Integer := items.length - 1
let idx: Integer := -1
let steps: Integer := 0
from until lo > hi or idx >= 0 do
let mid: Integer := (lo + hi) / 2
steps := steps + 1
if items.get(mid) = target then
idx := mid
elseif items.get(mid) < target then
lo := mid + 1
else
hi := mid - 1
end
end
result := create Search_Result.make(idx >= 0, idx, steps)
ensure
hit_is_correct: not result.found
or items.get(result.index) = target
logarithmic_steps:
result.steps <= max_probes(items.length)
end
end
class App
feature
run() do
let lin: Linear_Search_Algo
:= create Linear_Search_Algo
let bin: Binary_Search_Algo
:= create Binary_Search_Algo
let items: Array[Integer]
:= [3, 7, 10, 14, 19, 21, 25, 30]
let l_hit: Search_Result := lin.find(items, 30)
let b_hit: Search_Result := bin.find(items, 30)
let l_miss: Search_Result := lin.find(items, 11)
let b_miss: Search_Result := bin.find(items, 11)
print("Linear hit steps: " + l_hit.steps)
print("Binary hit steps: " + b_hit.steps)
print("Linear miss steps: " + l_miss.steps)
print("Binary miss steps: " + b_miss.steps)
end
end
-- Run the driver
let app := create App
app.run
Expected pattern:
sorted_input)Run both strategies with:
Record:
Call binary search with unsorted inputs and observe contract behavior.
Question:
Add one more strategy for comparison:
Keep the same Search_Result format so results remain comparable.
Use evidence from your run logs:
This lab gives you concrete intuition for how much an algorithm depends on the structure of its data.
In Part IV, we study lists, sets and maps, trees, and graphs so algorithm choices are supported by the right representation.