The answer depends on the actual definitions above, especially the direction used for duplicate removal.
invertworks for an infinite binary relation enumerated by a lazy list: each output needs only one input pair, and swapping preserves distinctness.removedupis productive whenever the input has infinitely many distinct values and equality tests terminate. Each next new value occurs at a finite input position and passes finitely many earlier filters. In particular, it works on the stipulated distinct-element representation of an infinite binary relation. On an infinite list with only finitely many distinct values it emits those values but cannot certify that there is no next new value; for example, demanding the tail after the first result ofremovedup (repeat 1)diverges.relcompdoes not work for arbitrary infinite inputs. Its nested list comprehension can remain forever in the inner generator. Withr = [(i,0) | i <- [0..]]ands = [(0,j) | j <- [0..]], it emits only pairs whose first component is zero, starving every later outer value. Duplicate removal cannot repair this lack of fair enumeration.transclosedoes not work for a general infinite input: even its firstlength scannot finish. It waits for finite saturation before returning any result.
Thus . A fairer enumeration may improve the latter constructions, but would be a different algorithm; ordinary depth-first enumeration does not become fair through lazy evaluation alone.
Let be the minimum number of jumps from index zero to index , with denoting an unreachable position. The minimum forward-jump dynamic programming recurrence iswhere the minimum of an empty set is and . Compute it in increasing index order:
D[0] = 0
for j = 1, ..., n-1:
D[j] = INFINITY
for i = 0, ..., j-1:
if D[i] != INFINITY and A[i] >= j-i:
D[j] = min(D[j], D[i] + 1)
return D[n-1]For algorithm correctness, classify any route to by its penultimate index . Every jump advances, so , and legality requires . By the principle of optimality, a shortest prefix to can be used; this gives the lower bound represented by the recurrence. Conversely, a minimizing reachable predecessor supplies an actual route of length , proving attainability. Mathematical induction on therefore proves every stored value correct.
There are predecessor tests, and two arrays are unnecessary when only the number is required. ThusFor the source example, the computed array isWhen , zero jumps are needed even if the lone entry is zero. An unreachable final position returns . Testing
A[i] >= j-i also avoids forming a potentially overflowing sum i + A[i] in bounded implementations. A faster frontier-based algorithm is possible, but the displayed method directly supplies the requested dynamic programming solution.Build a maximum spanning tree by the decreasing-weight version of Kruskal's algorithm. For the specified distinct endpoints , find their unique graph path in by depth-first search, retaining predecessors to reconstruct it. Return that path and its minimum edge weight .
For correctness, choose an edge of weight on . Deleting it splits into two connected components, separating from . No edge across this cut of a graph can have : replacing by would give a spanning tree of greater total weight, contradicting maximality of .
Every – graph path must cross the same cut of a graph, so it contains a crossing edge with . Therefore its minimum edge weight is at most . The tree route attains , provingThis is precisely the widest path problem. The proof allows tied, zero and negative edge weights. It depends on maximizing the total tree weight, not on a shortest-distance algorithm.
Let and . Sorting and Kruskal's algorithm take time; the tree search adds . HenceFor a connected simple graph with at least two vertices, , so the time is also . The expression remains valid when parallel edges are permitted. If the two endpoints coincide, the empty path is returned, with bottleneck under the usual empty-minimum convention; otherwise connectedness guarantees a nonempty route.
Programming language polymorphism permits one interface or piece of code to work with values of several types, while providing the behavior appropriate to those values. For example a routine that calls
play through a Player reference can use a Regular, Optimistic, or House object without knowing its concrete software class.Use programming language polymorphism when clients should depend on a common contract while concrete behavior varies, or when an algorithm is independent of the element type. It avoids duplicating client logic or repeatedly inspecting concrete runtime types, and supports extensions behind the same interface. A shared interface must still have meaningful behavioral guarantees: merely giving unrelated operations the same name does not establish that implementations can safely replace one another.
Creational patterns organize object creation; behavioral patterns organize interactions and responsibility for behavior. A creational pattern controls how concrete software objects are selected, instantiated, or shared, so clients need not hard-code the entire construction policy. Examples include the factory method pattern and the singleton pattern.
A behavioral pattern describes how objects collaborate, receive requests, or distribute an algorithm's behavior. For example the observer pattern lets a subject notify dependants after its state changes, and the strategy pattern supplies interchangeable implementations of an operation. This distinction concerns the pattern's principal responsibility; a system can use both kinds together.
Use diagonal merging of lazy lists: introduce one new row each round, and advance every active row once. Each round contains finitely many active rows, even when the outer list is infinite.
fairConcat :: [[a]] -> [a]
fairConcat = concat . levels []
where
levels [] [] = []
levels active pending =
let (new,rest) = case pending of
[] -> ([],[])
r:rs -> ([r],rs)
live = [r | r@(_:_) <- active ++ new]
in map head live : levels (map tail live) restNumber rows and positions within each row from zero. An occurrence at is emitted in round : its row enters in round and advances once per subsequent round. All earlier rounds and each round's output are finite, so every required occurrence appears after finite work.
live discards only empty rows, and head emits only existing entries; no new value is invented.Simply nesting
interleave is unsafe here: foldr interleave [] ([1,2] : repeat []) emits 1 and then demands an undefined merge of the infinitely many empty rows before it can emit 2. The finite-round construction avoids that starvation. There is still an unavoidable termination qualification: after the last actual output, infinitely many empty pending rows cannot be certified empty in finite work. The result can then have an undefined tail instead of a terminating []. Every required occurrence has nevertheless appeared; no generic algorithm can promise a proper completed output in that exceptional situation, by the absence-detection obstruction for infinite lists.Restrict the unknown function to the possibilities if , and otherwise. Each has exactly one essential variable, even if the allowed alphabet has more than two values. Every oracle answer in this restricted family is one bit.
A correct adaptive deterministic algorithm must distinguish all possibilities. A binary decision tree of depth has at most leaves, so . Therefore . Restricting the answer family is essential to the argument: unrestricted integer-valued answers need not have binary branching. Combined with part ii, this gives optimal logarithmic query complexity for fixed .
Let be the final
A set produced by the algorithm. It contains the initial A set, and every vertex outside has at most half its neighbours in : otherwise its final key would still be positive. Thus is closed under the activation rule.Now consider any legal sequence, including sequences which change some
A preferences back to B. We prove by induction that its current A set is always a subset of . This is true initially. A switch to B preserves the inclusion. If a vertex outside were to switch to A, its A neighbours would all lie in , so their number could not exceed half its degree. Such a switch is forbidden. A switch inside also preserves the inclusion.Consequently every attainable final
A set has size at most . The algorithm supplies a legal sequence attaining itself, so its sequence is optimal. This closure dominance for asynchronous threshold switching proves more than a local-greedy claim: temporary losses of A vertices cannot unlock an additional vertex outside the activation closure.The printed formula for the edge label has mismatched variable names. The type-correct reading is . Interpret the requested route as a directed walk of consecutive labelled edges, allowing repeated vertices; the distinction from a vertex-simple directed path is discussed below.
Let be the set of vertices reachable from the specified start after reading exactly the first labels. Initialize and updateAfter stages, answer yes exactly when . A directed walk realizing a prefix decomposes into a walk realizing the shorter prefix and one correctly labelled final edge; conversely every such extension is valid. This proves the dynamic programming invariant by induction on . Store reachable vertices in Boolean arrays and scan the edges per stage. The running time is with rolling-table space complexity, in addition to graph storage. The empty label sequence is realized by staying at .
If “path” is instead required to mean vertex-simple, augment the state to , where is the set of visited vertices. Start with and extend a matching edge to only when , replacing by . This gives an exact dynamic programming algorithm for that interpretation too; reject immediately when . A straightforward bound is running time with rolling-table storage. The exponential distinction is substantive: all labels equal and include the Hamiltonian path problem from a fixed start. The efficient layered recurrence answers the consecutive-edge interpretation without pretending to enforce distinct vertices.
Store the equal bucket at each internal node:The representation invariant for a tree produced by
data Tree a = Empty | Node (Tree a) [a] (Tree a)
toTree :: Ord a => [a] -> Tree a
toTree [] = Empty
toTree (pivot:xs) =
let (lo,eq,hi) = partition3 pivot xs
in Node (toTree lo) (pivot:eq) (toTree hi)
fromTree :: Tree a -> [a]
fromTree Empty = []
fromTree (Node left equal right) =
fromTree left ++ equal ++ fromTree righttoTree is that its bucket is nonempty and contains equal keys, every left-subtree key is smaller, and every right-subtree key is larger. Its multiplicities are preserved. Substituting the two equations for toTree into fromTree gives exactly the two defining equations for sort; induction proves sort = fromTree . toTree on finite inputs.Both versions call the same partition helper on the same pivot and sublists. The flattening stage makes no key comparisons. Thus fully producing the answer makes the same comparison decisions as the direct algorithm; no additional comparison against stored tree keys is introduced.
Use the two-pass strongly connected component algorithm: first run depth-first search on the original directed graph, then search its graph transpose in decreasing order of the first pass's finishing times. The first-pass order from part (a) isReversing the edges gives outgoing adjacency listsThe second pass first visits only . Its next new root is , whose search visits ; later entries already marked by this search are skipped. The next new root is , whose graph transpose neighbours have already been assigned. Therefore the components areWithin the middle component the directed cycle demonstrates mutual reachability. The source edge structure does not provide return paths from or to . More generally, decreasing finish order chooses a remaining component whose graph transpose search cannot escape to another still-unassigned component, so each second-pass tree is exactly one component. This explains the partition rather than merely listing it. The algorithm takes with adjacency lists.[ref]
Depth-first search by Jeff Erickson. §§6.5–6.6, strong components, condensation and the two-search construction
Remove each prime power as soon as it is found, and stop trial division once the remaining value has no possible smaller factor:At an outer-loop boundary, write , where is the product of the prime powers already removed. The invariant states that
def divisors(n: Int): Int = {
require(n >= 1)
var remaining = n
var factor = 2
var result = 1
while (factor.toLong * factor.toLong <= remaining.toLong) {
var exponent = 0
while (remaining % factor == 0) {
remaining /= factor
exponent += 1
}
result *= exponent + 1
factor += 1
}
if (remaining > 1) result *= 2
result
}result is , every prime divisor of is smaller than factor, and remaining has no prime divisor smaller than factor. Thus if factor divides remaining, it is prime; a composite factor would have a smaller prime divisor, contrary to the invariant.During the inner loop, with fixed, . Each division removes one copy, so the positive remainder strictly decreases and supplies an inner-loop variant. On leaving the inner loop the complete power has been removed; multiplying the result by
exponent + 1 uses part (d) and reestablishes the outer invariant after advancing factor. The outer factor increases and can never exceed the original while its guard holds, proving termination.At exit, a remainder greater than one must be prime: a composite remainder would have a prime factor no larger than its square root, which would still be smaller than
factor. It therefore contributes one final factor of two. A remainder of one contributes nothing. The algorithm returns using constant auxiliary space and no array. The widened multiplication in the loop guard prevents an Int overflow in the square test.The iterator pattern exposes sequential traversal through a small interface while hiding the representation of the underlying data. A client asks whether another entry is available and requests the next entry; it need not know whether the producer uses an array, linked list, tree or generated sequence.
The key design benefit is encapsulation of traversal state and representation. The same consuming algorithm can work through the interface for different producers, and each traversal can keep its own cursor state when a fresh iterator is supplied. This independence requires separate consumption states; two references to one iterator share a cursor.
Path compression 2026-10-18
Path compression changes parent pointers during a disjoint-set find operation so that future queries traverse fewer edges. Full path compression first follows the entire parent route to its root of a tree, then makes every member on that route point directly to that same root of a tree. No member changes its block of a set partition or its disjoint-set representative. Pointers outside the traversed route stay as they were. The following iterative Python implements full path compression and union by rank;
parent and rank are initially indexed arrays with parent[x] = x and rank[x] = 0 for every valid member.def find(x):
root = x
while parent[root] != root:
root = parent[root]
while parent[x] != x:
following = parent[x]
parent[x] = root
x = following
return root
def unite(x, y):
a, b = find(x), find(y)
if a == b:
return False
if rank[a] < rank[b]:
a, b = b, a
parent[b] = a
if rank[a] == rank[b]:
rank[a] += 1
return TrueStarting from the displayed disjoint-set forest,
find(3) traverses , returns and changes only from to . The route still has two edges. Then find(5) changes only from to . The final parent array is ; every stored rank retains its displayed value. The rooted tree at now has height one although its root's rank is two. Root still represents a separate singleton set.find(3) followed by find(5). Labels record stored ranks, including historical ranks at nonroots. Details.The two-pass algorithm first discovers the answer and then reuses it. During the second pass, saving
following before overwriting the pointer preserves the remaining route. Every replacement pointer targets the original root of a tree, proving that the partition of a set is unchanged.Full path compression is one specific rule. The authored
UF.java implementation instead uses path halving, redirecting a visited member to its parent's parent while advancing along the route. Its union by rank rule agrees with the one above.[ref]UF.java. Algorithms, 4th edition by Robert Sedgewick and others; UF.java, find and union methods: halving assignment versus the full-compression algorithm here; lower-rank linking and equal-rank increment
Paul C. van Oorschot 2026-11-05
Paul C. van Oorschot coauthored the Handbook of Applied Cryptography by Alfred J. Menezes, Paul C. van Oorschot and Scott A. Vanstone. Its treatment connects cryptographic constructions with explicit mathematical algorithms and stated assumptions, rather than describing encryption only as a black box. The number-theoretic chapter includes algorithms for modular square roots, while the public-key chapter gives RSA cryptosystem and Rabin cryptosystem constructions.[ref]
For a concrete special case, if is prime and is a nonzero square modulo , a square root is . This illustrates how assumptions on an integer modulus determine which algorithm and correctness argument apply.
Paul W. Goldberg 2026-10-31
Computer scientist whose work includes algorithms, computational complexity theory and algorithmic game theory.
Official profile: www.cs.ox.ac.uk/people/paul.goldberg/
Perfect elimination ordering 2026-10-14
A perfect elimination ordering of a finite simple graph is a permutation of its vertices such that each set of later graph neighboursis a clique. Thus is a simplicial vertex in the remaining induced subgraph . Empty and one-vertex sets satisfy the clique condition automatically. The order here is the order of deletion: goes first.
The characterization is powerful: a finite simple graph is a chordal graph if and only if it has a perfect elimination ordering. For one proof outline, the earliest vertex of an induced cycle of length at least four would have two later graph neighbours on that graph cycle that are not adjacent, contradicting the clique condition. Conversely, every nonempty chordal graph has a simplicial vertex, and every induced subgraph of a chordal graph is a chordal graph; repeatedly deleting such a vertex supplies the ordering.[ref]
In the square-plus-diagonal chordal graph above, is a perfect elimination ordering, with later graph neighbourhoods , , , and . Starting instead with fails: its graph neighbours do not form a clique, because is absent. Thus even a chordal graph can have a poor elimination ordering.
For a larger example, join three triangles along shared edges, usingThe exact graph neighbourhoods are , , , , and . The ordering has later graph neighbourhoods , , , , and , all cliques; this certifies that the graph is a chordal graph without enumerating its graph cycles. Its maximal cliques , , form a junction tree in that order, with intersections and .
A perfect elimination ordering removes three overlapping triangles without fill-in; choosing the central vertex first adds three edges
. Details. To find an ordering, maximum cardinality search starts with all scores zero, repeatedly selects an unselected vertex of largest score, and increases each unselected graph neighbour's score by one. Reverse the selection order to obtain a candidate perfect elimination ordering. For the three-triangle graph, one valid selection sequence is , with scores at selection ; reversing it gives the displayed deletion order. On a chordal graph every tie choice works. On an arbitrary graph, test the resulting later graph neighbourhoods for the clique condition to recognize chordal graphs. Suitable adjacency lists, score buckets and a linear-time verification method give recognition.[ref]
The deletion rule also explains sparse Gaussian elimination. For a symmetric positive-definite matrix whose matrix graph is , eliminating pivot applies the Schur complement updateOnly pairs of remaining graph neighbours of can acquire new off-diagonal entries. A perfect elimination ordering already has all those edges, so Cholesky decomposition creates no symbolic fill-in. Positive definiteness ensures nonzero pivots in exact arithmetic. Special numerical cancellation can suppress predicted entries, and floating-point rounding affects their values.[ref]
Chordal Graphs and Semidefinite Optimization by Lieven Vandenberghe and others. §9.1; equation 9.2; Theorem 9.1
In the three-triangle graph, deleting first instead connects every pair in and adds exactly . The graph was already a chordal graph; the extra fill-in comes from the ordering.One further reward is optimal graph coloring. Process a perfect elimination ordering backwards and give each vertex the smallest positive color absent from its already colored graph neighbours. Those graph neighbours form a clique, so this greedy algorithm uses at most the clique number colors; any clique of that size requires at least that many. In the three-triangle graph, coloring gives colors , so both the chromatic number and the clique number are three. Every maximal clique also occurs among : take its earliest vertex and use maximality. The same ordering therefore supports graph coloring, maximal clique extraction and sparse computation.
Historically, Donald J. Rose, Robert E. Tarjan and George S. Lueker's 1976 paper connected graph elimination to symmetric sparse matrices and gave a search-based algorithm for finding a perfect elimination ordering when one exists. Its abstract distinguishes a minimal elimination ordering from a minimum one: removing avoidable fill-in does not by itself minimize the total number of added edges.[ref]
Peter Ljunglöf Created 2026-10-14 Updated 2026-11-05
Peter Ljunglöf is a computer scientist and a coeditor, with Alex Gerdes and Jonas Almström Duregård, of Data Structures and Algorithms edited by Peter Ljunglöf and others. The book develops abstract data types alongside concrete representations and analysis of algorithms. Its comparison of linear search with binary search illustrates how assumptions about the stored data affect the available algorithm.[ref]
Homepage: www.chalmers.se/en/persons/peb/
Peter Shor Created 2026-10-20 Updated 2026-11-05
Peter Shor is a mathematician whose research includes quantum computing, quantum information theory, algorithms and computational geometry.[ref] His quantum algorithms for integer factorization and discrete logarithms require a number of steps polynomial in the number of input digits. They made the distinction between classical and quantum computational resources concrete for problems used in cryptography.[ref]
Portrait of Peter Shor
. International Centre for Theoretical Physics. CC BY 3.0, via Wikimedia Commons. Source. Peter Spirtes 2026-11-05
Peter Spirtes develops mathematical and computational methods for causal discovery. His work asks which causal conclusions can be recovered from statistical data and background knowledge when fully controlled experiments are unavailable, and how to turn the corresponding guarantees into practical algorithms.[ref]
Peter Spirtes by Carnegie Mellon University. Research: the two goals of the Tetrad project and limitations of its assumptions
With Clark Glymour and Richard Scheines, he coauthored Causation, Prediction, and Search by Peter Spirtes, Clark Glymour and Richard Scheines. One important example is the PC algorithm, which uses conditional independence information to remove candidate edges and orient some of the remaining ones. Under its assumptions, the resulting graph represents an equivalence class of possible causal explanations; it need not recover every arrow in a unique graph. Making these limitations explicit is part of the method's contribution, rather than an exception to it.[ref]
Causation, Prediction, and Search by Peter Spirtes, Clark Glymour and Richard Scheines. Chapter 5, section 5.4.2, PC algorithm; Markov equivalence and the stated causal assumptions
Portrait of Peter Spirtes
. Portrait selected from the matching Carnegie Mellon institutional profile. Source.




