Algorithm Created 2026-09-24 Updated 2026-10-13
An algorithm is a finite, precise procedure for transforming an input into an output.
Here "finite" refers to the description: a short algorithm can perform arbitrarily many steps on increasingly large inputs. Solving a computational problem additionally requires termination and the specified output on every permitted input. State assumptions explicitly: binary search requires a sorted array.
An algorithm is an idea that can have implementations in different programming languages. Its explanation should make three things visible: the permitted inputs, the rule for each step, and the meaning of the returned output. Algorithm correctness then separates finding the right answer from eventually finishing; time complexity asks how much work finishing requires.
A particularly useful design question is: what knowledge survives each step? In binary search, it is the location of every possible match inside the remaining candidate range. Writing that fact as a loop invariant turns an intuitive shortcut into a procedure whose discarded candidates can be justified. This often helps discover the algorithm, rather than merely check it afterward.
Binary search 2026-10-13
Binary search finds a target in a sorted array by inspecting the middle of the remaining candidate range. If the middle entry is smaller than , every entry to its left is also too small; if larger, every entry to its right is too large. One comparison therefore rules out many entries. Equal entries are allowed; this version returns any matching index, without promising the first occurrence.
Use indices starting at zero and inclusive endpoints . Initially , , where is the array length. While , choose
The brackets denote the floor function. Compare with : return on equality; set if ; otherwise set . If , report absence. The updates exclude the already inspected middle entry. This also handles an empty array, whose initial endpoints are . Sedgewick and Wayne specify a sorted array as input and return a matching index or for absence.[ref]
For a fully worked example, take the sorted array
Search for :
StepCandidate indicesMiddle index and valueConsequence
1, : retain .
2, : retain .
3, Return index .
For , the first two steps agree. Next retains only index ; inspecting sets . Now , so the answer is "absent" after four comparisons.
Figure 1.
Binary search retains only candidates compatible with every comparison
. Blue cells are current candidates, orange marks the middle entry, and green marks a match. Both searches use the same fifteen-entry sorted array. Details.
The loop invariant is: the array stays sorted, , and every index containing lies between and . It holds initially. The sorted array property justifies each exclusion. Returning on equality gives a match; an empty candidate range implies absence. Crucially, this loop invariant allows to be absent from the beginning.
For a nonempty range of size , an unsuccessful comparison leaves at most candidates. Thus sizes satisfy after unsuccessful comparisons. This proves termination and, for , at most middle-entry comparisons. The final singleton still needs inspection: requires one comparison, not zero. For , the bound is four. This is the halving argument behind logarithmic time complexity.[ref]
A decision tree with binary tests has at most leaves. Distinguishing possible matching positions therefore requires . This lower bound counts binary tests, whereas the bound above counts three-way comparisons.
Exponentiation by squaring Created 2026-10-03 Updated 2026-10-13
Exponentiation by squaring computes an integer power by reading the exponent's binary expansion. Appending a binary digit to a prefix of value replaces by , so the corresponding power changes by
Each new digit requires one squaring and, only for digit one, one further multiplication. The number of digits grows with the logarithm of the exponent, making this useful for modular exponentiation, primality testing, and powers of matrices using matrix multiplication.[ref]
For a concrete calculation, , so . The optimized left-to-right algorithm used here instead keeps one accumulated power: initialize from the leading digit one, then process only the remaining digits :
Thus , using three squarings and two further multiplications, instead of twelve successive multiplications starting at .
Figure 1.
Reading the same binary expansion computes and
. Each appended binary digit changes the prefix value by . The ordinary-power row grows to ; the modular exponentiation row stores only remainders . Both traces use three squarings and two further multiplications. Details.
For a general positive integer with leading digit , set and, for , first replace by , then multiply it by if . Return . This explicitly skips the leading digit's otherwise unnecessary operations on one. A Computational Introduction to Number Theory and Algebra by Victor Shoup (Section 3.4, pp. 65–66) presents the version initialized at one and processing every digit; the optimization here makes the counting convention unambiguous.
The loop invariant is , where is the value of the digits already read. Initially and . If the next digit is , then squaring and optionally multiplying give , exactly the next prefix. At termination . This proof uses associativity of multiplication; it works in any monoid, including matrices with the identity matrix as identity element, and needs no commutativity between different bases.
For , return the identity element without performing a multiplication. This is the empty-product convention ; if , the algorithm therefore returns one for by convention, rather than resolving its meaning in every mathematical setting. Negative integer exponents require an inverse element: compute , counting the inversion separately. Over the integers, such an inverse generally leaves the integers, and a zero base has no inverse.
Insertion sort 2026-10-13
Insertion sort grows a sorted prefix of an array one entry at a time. In the adjacent-swap version, start with position , compare its current sort key with the preceding sort key, and swap the complete sorting records only when the right sort key is strictly smaller. Continue leftward until no swap is needed or the entry reaches position zero; then process original position . A tie stops the pass. This is the strict comparison and exchange rule in the authored implementation.[ref]
Take , where the subscript labels a complete sorting record and comparisons inspect only its sort key. Starting from index zero, the complete key-comparison trace is:
ComparisonRight sort key smaller than left?ActionArray after the action
1, insert position : yesSwap positions .
2, insert position : noStop this pass on a tie.
3, insert position : yesSwap positions .
4, continue leftward: yesSwap positions .
5, continue leftward: noStop this pass on a tie.
Figure 1.
Insertion sort: five sort key comparisons and three complete-entry swaps
. Each row after the input shows the state after one comparison. Orange cells mark an adjacent swap; green cells mark a stopping tie. Identity letters are not compared. Both equal-key groups retain their input order, demonstrating stable sorting. Details.
The loop invariant is that, before pass , positions satisfy sortedness and contain precisely the entries originally in that prefix. Moving the next entry left through strictly larger sort keys extends that prefix, and every swap retains sorting permutation preservation. Equal-key entries never cross, giving stable sorting. Each leftward step decreases the position, and each completed pass increases the prefix length, so the algorithm terminates.
With constant-cost key comparisons and swaps, reverse-ordered distinct sort keys cause comparisons and swaps. Thus worst-case time complexity is in Big O notation. An already sorted array needs only key comparisons for and no swaps. The adjacent-swap version uses constant auxiliary space complexity. Small or nearly sorted arrays can therefore make insertion sort useful despite its quadratic worst case.
Mathematical induction Created 2026-09-24 Updated 2026-09-24
Merge algorithm 2026-10-13
The merge algorithm combines two finite sorted sequences under the same total order, retaining every entry exactly once. Keep a position at the first unconsumed entry, or "head", of each input. While both heads exist, compare their keys, append the smaller head to the output, and advance only that head's position. On equality take the left head. Once either sequence is exhausted, append the other remainder without further key comparisons. Empty inputs therefore require no head comparisons. The MIT notes illustrate this two-head combination step.[ref]
For example, the merge algorithm applied to and returns :
Figure 1.
Five head comparisons in the merge algorithm
. Orange heads are chosen and arrows lead to the next output entry. The compared head pairs are (1,2), (4,2), (4,3), (4,8) and (7,8). The left sequence then runs out; append (8,9) without further key comparisons. Details.
The head pairs , , , and select respectively. After the fifth comparison the left sequence is empty, so is appended. Seven entries are written, but only five head comparisons occur: tests for exhaustion and appending the remainder are separate operations. This distinction makes the example's count smaller than the bound .
For nonempty inputs of lengths , each head comparison consumes exactly one entry. After such removals at most one entry remains, so both heads cannot still exist. Hence the merge algorithm uses at most head comparisons. The bound can be attained: and require six head comparisons before the left sequence is exhausted. With constant-cost comparisons, access and copying, writing all entries gives time complexity in Big O notation. Writing a separate output array needs entry slots; the two positions require only constant additional storage.
The merge algorithm is useful whenever two already ordered collections must become one: it reads each input only forwards. This works with arrays and linked lists, without needing access to arbitrary middle positions. Choosing left on ties preserves each input's internal order and places equal left entries before equal right entries, which is the combination rule needed for stable sorting in merge sort.
The loop invariant behind algorithm correctness is that the emitted prefix is sorted, contains exactly the consumed entries, and every emitted key is at most every unconsumed key. Each sorted sequence has its smallest remaining key at its head, so the smaller of the two heads is safe to append. The invariant survives each removal; when one sequence ends, the other sorted remainder is safe to append in full. Each comparison removes an entry, so termination follows from the finite input lengths.
// Target: computer-science.bigb
Modular exponentiation Created 2026-10-06 Updated 2026-10-14
Modular exponentiation computes the remainder for an integer base , a nonnegative integer exponent , and, here, an integer modulus . It applies exponentiation by squaring while reducing after each multiplication, including each squaring. The result is the unique representative in . There is no requirement that the base be coprime to the modulus when the exponent is nonnegative.[ref]
Take , , and . Initialize ; the remaining digits give
Hence . The final operation multiplies by , since the preceding square has already been reduced. The computation never needs to construct .
In general reduce the base first, . For , initialize and process the remaining binary digits with , followed, for a digit one, by . The loop invariant is
where is the processed prefix value. The congruence is preserved because modular arithmetic respects multiplication: if , then and . Thus changing representatives never changes the final congruence class.
Both factors in every product are less than , so the unreduced product is less than , and the stored result is again less than . The same bounds hold for a gigantic exponent, such as : there are squarings, each on bounded-size residues. For base two, the corresponding ordinary power would have binary digits. The benefit is avoiding the huge ordinary power altogether, rather than constructing it and taking one final remainder. Initial reduction is essential when the supplied base is much larger than the modulus.
For , return under the same empty-product convention as exponentiation by squaring. For , require , find the modular multiplicative inverse , and compute ; the extended Euclidean algorithm can find that inverse. Its cost is separate from the positive-exponent binary powering multiplication count. For example, , while is undefined because and are not coprime.