Everything on the site.

Big-O notationAlgorithms · Count the steps in real loops, then compare n, n log n, n² and 2ⁿ on a live chart.RecursionAlgorithms · Watch frames push and pop on the call stack for factorial and Fibonacci.BFS vs DFSAlgorithms · A queue against a stack: draw a graph and see which order each search visits it in.Sorting algorithmsAlgorithms · Seven sorts on the same input, with comparison, swap and write counts side by side.Binary searchAlgorithms · Halve the range, probe the middle, repeat: with lo, mid and hi drawn on the array.Sets, relations and functionsDiscrete maths · Shade any set expression on a Venn diagram and test a relation for its properties.Proof by inductionDiscrete maths · Four worked proofs revealed one step at a time, and a broken one to diagnose.CombinatoricsDiscrete maths · An exact nPr and nCr calculator, and a Pascal’s triangle that shows its own addition rule.Boolean algebraDiscrete maths · The laws, De Morgan, and normal forms, with a truth-table generator built in.Finite-state machinesTheory · Build a DFA in a table, see it drawn, and run strings through it one symbol at a time.Domain theoryTheory · Why recursion needs least fixed points, and how CPOs, Scott continuity and powerdomains supply them.Truth table generatorTools · Expression in, truth table out: with DNF, CNF, a minimal form and LaTeX.Graph traversal visualiserTools · Draw a graph, then animate BFS, DFS and Dijkstra with the frontier shown.Recursion tree visualiserTools · Paste a recursive function or a recurrence and see its call tree and call count.Gradient descentML maths · Drag the starting point, set the learning rate, and watch it converge or blow up.Vectors and embeddingsML maths · Bend the plane with a 2×2 matrix and measure how close toy word vectors are.Softmax and cross-entropyML maths · Edit the logits and watch the probabilities, the loss and the gradient update live.AttentionML maths · Q, K and V for three tokens, a live score heatmap, and the weighted sum of values.Formatting a CS paper in LaTeXLaTeX · Page limits, \documentclass, front matter, theorem environments and the bibliography.Camera-ready checklistLaTeX · Fonts embedded, PDF/A, copyright form, source bundle, and why final versions get rejected.LaTeX templatesLaTeX · Where the official classes live, plus a minimal article template you can copy.AlgorithmsSection · Step-through visualisations of sorting, searching, recursion, graph traversal and Big-O growth, each with pseudocode that highlights the line being run.Discrete mathsSection · Sets, relations and functions, proof by induction, counting, and Boolean algebra, explained with worked examples and small interactive checkers.TheorySection · Finite-state machines, automata and the mathematical semantics of programming languages, explained from first principles.ML mathsSection · Gradient descent, vectors and embeddings, softmax and cross-entropy, and attention, each with a formula, a worked numeric example and a live version.ToolsSection · A truth-table generator with CNF, DNF and LaTeX output, a graph-traversal visualiser for BFS, DFS and Dijkstra, and a recursion-tree visualiser.LaTeXSection · How to format a CS conference or workshop paper in LaTeX, a camera-ready checklist, and a minimal template you can adapt.
Algorithms

Binary search, step through it on a sorted array

Binary search finds a value in a sorted array by repeatedly checking the middle element and throwing away the half that cannot contain it. A million elements need at most 20 checks. The idea is simple; getting every boundary right is famously not.

Interactive: Binary-search stepper with classic and lower-bound variantsUpdated

Think of looking up a word in a paper dictionary. You don’t read from the first page: you open it in the middle, see whether your word comes before or after, and repeat in the half that’s left. Binary search is that strategy made precise. It only works because the data is sorted, which is what lets one comparison rule out half the remaining candidates.

Step through it

The array below is searched for 63. lo and hi mark the range that could still contain the target; mid is the element being probed. Faded cells have been ruled out. Try a target that is not in the array, then switch to the lower-bound variant.

Binary search stepperValues are sorted for you. Try a target that is missing, too.

  • lo
  • mid (probe)
  • hi
  • Ruled out
  • Answer

Pseudocode

    Variables

    Probes

    0So far
    0Worst case
    0Linear scan

    Worked example: searching for 63

    The array is 3, 8, 15, 21, 27, 34, 42, 50, 57, 63, 71, 79, 86, 94, 99 (indices 0 to 14).

    ProbelohimidA[mid]Decision
    101475050 < 63, so lo ← 8
    2814117979 > 63, so hi ← 10
    3810963Found at index 9

    Three probes instead of the ten a left-to-right scan would need.

    Why it is so fast

    Each probe either finds the target or at least halves the number of candidates. Starting from nn candidates, after kk probes at most n/2kn / 2^k remain, so the search ends after at most ⌊log⁡2n⌋+1\lfloor \log_2 n \rfloor + 1 probes. That is O(log⁡n)O(\log n) time, against O(n)O(n) for scanning.

    nLinear scan, worst caseBinary search, worst case
    10104
    1001007
    1,0001,00010
    1,000,0001,000,00020
    1,000,000,0001,000,000,00030

    The catch is the precondition. Sorting costs O(nlog⁡n)O(n \log n), so binary search pays off when you search the same data many times, or when it arrives sorted anyway.

    Why it is correct

    The loop maintains one invariant: if the target is anywhere in the array, it is in A[lo..hi]A[lo..hi]. It holds at the start, when the range is the whole array. Each probe keeps it: if A[mid]<targetA[mid] < \text{target}, then because the array is sorted, everything at or left of midmid is also too small, so moving lolo to mid+1mid + 1 loses nothing. The other case is symmetric.

    The loop also terminates, because hi−lohi - lo shrinks on every iteration. When the range is empty (lo>hilo > hi) the invariant says the target is nowhere, so “not found” is the right answer. Invariant plus termination is the standard way to prove a loop correct, and it is the loop version of proof by induction.

    Where it goes wrong

    Jon Bentley reported that when he asked professional programmers to write binary search, most of their versions had bugs. The usual suspects are:

    • Mixing up the boundaries. Decide whether hi is inclusive (the last candidate) or exclusive (one past it) and stick to it. The classic version on this page is inclusive (while lo ≤ hi, hi ← mid − 1); the lower-bound version is exclusive (while lo < hi, hi ← mid). Mixing the two styles creates infinite loops or skipped elements.
    • Not making progress. Writing lo ← mid in the inclusive version can loop forever when lo and hi are neighbours, because mid rounds down to lo.
    • Overflow. In languages with fixed-size integers, (lo + hi) / 2 overflows when both are large. Writing lo + (hi − lo) / 2 avoids it. The JDK’s own binary search had exactly this bug until 2006.

    Lower bound: the first position that is at least the target

    Often you want more than “is it there?”: where is the first copy of a repeated value, or where would a new value be inserted to keep the array sorted? The lower-bound search answers both. It returns the smallest index ii with A[i]≥targetA[i] \ge \text{target} (or nn if there is none). It never stops early, so it always uses about log⁡2n\log_2 n probes, and it finds the leftmost of several equal values. Python’s bisect_left and C++’s std::lower_bound are this function.

    Binary search on the answer

    The idea works on any yes/no question whose answer flips exactly once as a number increases. To find the integer square root of NN, the largest xx with x2≤Nx^2 \le N, search over xx instead of over an array: the test “x2≤Nx^2 \le N?” is true up to the answer and false after it.

    Worked example: integer square root of 50

    Keep lolo as a value known to pass and hihi as the largest value not yet ruled out, starting from [0,50][0, 50]. Because a passing probe sets lo←midlo \leftarrow mid, round the midpoint up, or the range can stop shrinking.

    lohimidmid²Decision
    05025625too big: hi ← 24
    02412144too big: hi ← 11
    011636fits: lo ← 6
    611981too big: hi ← 8
    68749fits: lo ← 7
    78864too big: hi ← 7

    Now lo=hi=7lo = hi = 7, and indeed 72=49≤50<64=827^2 = 49 \le 50 < 64 = 8^2.

    Where else this shows up Finding the first failing commit in a version history (git bisect), the smallest capacity that fits a workload, or the largest speed that keeps an error under a threshold are all binary search on the answer.

    References

    1. D. E. Knuth, The Art of Computer Programming, Volume 3: Sorting and Searching, 2nd ed., Addison-Wesley, 1998, §6.2.1.
    2. J. Bentley, Programming Pearls, 2nd ed., Addison-Wesley, 2000. Column 4 is about writing a correct binary search.
    3. “Binary search”, Wikipedia.