Skip to content
← Production DevOps & SRE

Learning bite

Binary search and timestamp boundaries

Find a half-open interval in sorted observations and make ordering assumptions explicit.

  • Python
Documentation reviewed2026-10-01 · 3 min read
  1. Why
  2. Predict
  3. Try
  4. Inspect
  5. Explain
  6. Save evidence

Predict before you try

Can a result that works for one small example establish the algorithm or design works at its boundary?

Try it: Establish the ordering contract →
On this page

Establish the ordering contract

Binary search requires ordered data or a monotonic predicate. Logs merged from different processes are not automatically sorted; clocks, buffering, and delayed delivery can change their order. This exercise uses finite, nondecreasing integer timestamps in one agreed unit. Check those assumptions once when loading a dataset.

Use a half-open interval [start, end) so adjacent query windows do not double-count the boundary. A lower bound finds the first position whose value is at least the target, or the list length if none exists.

Keep one invariant

Save as time_search.py:

python
def lower_bound(values, target):
    lo, hi = 0, len(values)
    while lo < hi:
        mid = (lo + hi) // 2
        if values[mid] < target:
            lo = mid + 1
        else:
            hi = mid
    return lo


def interval_indices(values, start, end):
    if start > end:
        raise ValueError("start must not exceed end")
    return lower_bound(values, start), lower_bound(values, end)


times = [10, 10, 20, 35, 60]
left, right = interval_indices(times, 10, 35)
assert (left, right) == (0, 3)
assert times[left:right] == [10, 10, 20]
assert interval_indices([], 0, 1) == (0, 0)
assert interval_indices(times, 20, 20) == (2, 2)

At each step, positions before lo have values below the target and positions at or after hi have values at least the target. The candidate boundary remains between them. Each iteration shrinks the interval; when they meet, the boundary is known.

Two bounds take O(log n) query work on a random-access list and constant auxiliary space. Returning the slice itself adds O(m) work and space for m results. Sorting raw observations first costs additional work, and inserting into a Python list still moves elements; binary search does not make insertion logarithmic.

Hand-trace a boundary query

For target 35 in [10, 10, 20, 35, 60], start at lo=0, hi=5. The midpoint is 2 with value 20, so move lo to 3. The next midpoint is 4 with value 60, so move hi to 4. The next midpoint is 3 with value 35, so move hi to 3. Both pointers meet at index 3: the first value at least 35. The upper query boundary excludes the value at that index.

Run python3 time_search.py, then try target 70: its insertion position is 5, the list length. Empty data also returns its length, zero. For the unsorted list [20, 10], target 15 can return the end even though 20 is present. That counterexample shows why the precondition matters; repeatedly validating the entire list inside every query would change the query cost.

Checkpoint and revision

Compare lower_bound with bisect.bisect_left. Test repeated timestamps, values outside the range, empty data, and equal boundaries. Show an unsorted counterexample and explain why validation belongs at ingestion if many queries reuse the same list. To understand a database index, inspect that database’s implementation separately; this exercise only covers binary search over a list.

Checkpoint answer: an equal start/end gives an empty interval; repeated timestamps at the start are included, and those equal to the end are excluded. Preserve those exact endpoint rules when moving to the next time-window exercise, whose interval deliberately uses a different open end.

Sources

Python bisect and performance notes↗ and Python sorting guidance↗.

Prove what you learned

Inspect the result

Retain input/assumptions, expected result, actual trace and complexity or design limits.

Explain it in your own words

Explain why your observation supports the stated outcome, and name one thing it does not prove.

Try a small variation

Change one boundary condition—empty input, duplicate value, interval edge or missing dependency—and explain the outcome.

Your evidence should show

  • Record the context/revision or label the work as a paper or prepared fixture.
  • Compare the prediction with the actual result from “Establish the ordering contract”.
  • Retain input/assumptions, expected result, actual trace and complexity or design limits.
  • Explain the transfer challenge and retain the same verification question.
Hint

State the input assumptions and trace an empty, repeated or limiting case.

Answer guide: compare after predicting

No. Explain correctness and cost or the design trade-off, and compare a declared boundary case.

Compare this with your actual observation. A paper answer is useful reasoning practice; record execution separately.

Recall bounded loops, data types and explicit failure handling in Python. Revisit the previous lesson.

Save the prediction, result and explanation ↓

Your notes and evidence

Record observations, questions, or links to your work. Keep credentials out of your notes.

Loading saved progress…

Back up or restore this path

Progress and notes stay in this browser. A backup contains only this learning path.