Kruskal's Minimum Spanning Forest and Tree Algorithm #
This module formalizes Kruskal's greedy algorithm for minimum-weight spanning forests and minimum
spanning trees on finite multigraphs over Fin n, together with its functional correctness,
global optimality, distinct-weight uniqueness, Cslib.DataStructure disjoint-set client
specification,
and TimeM comparison and disjoint-set step bounds.
Mathematical model and source mapping #
An input multigraph on n vertices is represented as a finite list es : List (WeightedEdge n W)
of undirected weighted edges (u, v, weight) with u v : Fin n (allowing parallel edges,
self-loops, disconnected graphs, and negative or duplicate weights). Connectivity is tracked via
Cslib.DataStructure.DisjointSet.Partition n:
edgePartitionFrom p esfoldsPartition.mergeacrossesstarting fromp, andedgePartition es := edgePartitionFrom (Partition.discrete n) esis the induced connected-component partition onFin n.IsAcyclic Fstates the exact component-edge conservation law(edgePartition F).numComponents + F.length = n: starting fromnisolated vertices, every edge inFmerges two previously distinct connected components. The companion moduleCslibFrontier.Algorithms.Lean.Kruskal.SimpleGraphbridgesIsAcyclic,IsSpanningForest, andIsSpanningTreeto Mathlib'sSimpleGraph.IsAcyclic, maximal acyclic subgraphs, andSimpleGraph.IsTree.IsSpanningForest es FrequiresF.Subperm es,IsAcyclic F, andedgePartition F = edgePartition es(matching maximal forest subgraphs ofes).
Primary-source correspondence:
- Cormen et al., Chapter 23, Section 23.1 (Theorem 23.1, pp. 626–628, and Corollary 23.2,
p. 628) and Section 23.2 (
MST-KRUSKAL, pp. 631–633):[CLRS2009]formulates Theorem 23.1 andMST-KRUSKALfor connected, simple undirected graphs with real-valued edge weights. HerekruskalEdgessortsesin nondecreasing weight order viaList.mergeSortand scans the sorted list, accepting each edge whose endpoints lie in distinct components;kruskalEdges_isMinSpanningForestgeneralizes global weight optimality to arbitrary finite multigraphs (connected or disconnected, with parallel edges, self-loops, and negative or equal weights) over any[AddCommMonoid W] [LinearOrder W] [IsOrderedAddMonoid W], andkruskalEdges_isMinSpanningTreespecializes to connected inputs ((edgePartition es).numComponents = 1). - Kruskal, Opening paragraph, Construction A, and uniqueness proof, pp. 48–50:
Kruskal's original paper assumes a finite connected graph with distinct positive real edge lengths
and proves (
T = A_{n-1}, p. 50) that Construction A yields the unique shortest spanning subtree. Hereperm_kruskalEdges_of_isMinSpanningForest_of_nodup_weightsgeneralizes uniqueness (up to list permutation, hence exact edge-multiset equality) to arbitrary finite multigraphs (connected or disconnected) with distinct weights in any[AddCancelCommMonoid W] [LinearOrder W] [IsOrderedCancelAddMonoid W].
Cost model and limitations #
kruskalM executes Kruskal's algorithm in TimeM Cost with two separate operation counters:
Cost.comparisons: exact number of edge-weight comparisonsdecide (a.weight ≤ b.weight)performed byList.mergeSortM, bounded bym * Nat.clog 2 m(m = es.length) viaCslib.Algorithms.Lean.TimeM.time_mergeSortM_le.Cost.ufSteps: exact number of disjoint-set forest steps (findparent-pointer inspections plusunionroot links) recorded byCslib.DataStructure.DisjointSet.UnionFind.runonkruskalClient, bounded bym * (2 * Nat.log 2 n + 3)viaDisjointSet.UnionFind.run_correct(worst-caseO(log n)per operation from union by rank, rather than Tarjan's amortizedO(α(n))inverse-Ackermann bound).realized_kruskaladditionally decouples Kruskal's scan from any particular concrete forest: for anyCslib.DataStructure.StateTransitionImplandCslib.DataStructure.BoundforDisjointSet.spec nthere is an execution ofkruskalClient (sortEdges es)whose total cost is at most the certificate's total charge.
DisjointSet.UnionFind.init n (O(n) MAKE-SET initialization in [CLRS2009], MST-KRUSKAL
lines 2–3, pp. 631, 633), list splitting/allocation in List.mergeSortM and kruskalClient, and
pure Lean array updates inside Batteries.UnionFind carry 0 TimeM Cost charge (so Cost
records only sorting weight comparisons and post-initialization UnionFind find/union steps,
not Word-RAM instructions; if a Batteries.UnionFind value were shared non-linearly, array updates
could copy, whereas DisjointSet.UnionFind.run threads its state single-threadedly).
Main definitions #
WeightedEdge,totalWeight: weighted undirected multigraph edges onFin nand edge-list weight sum.edgePartitionFrom,edgePartition: connected-component partitions induced by an edge list.IsAcyclicFrom,IsAcyclic,IsSpanningForest,IsSpanningTree,IsMinSpanningForest,IsMinSpanningTree: forest, spanning-forest, spanning-tree, and minimum-weight predicates.kruskalScan,sortEdges,kruskalEdges: pure Kruskal scan, weight merge-sort, and Kruskal's algorithm.kruskalClient:DisjointSet.spec nclient for the Kruskal cycle-filtering pass.Cost,Output,sortEdgesM,kruskalScanM,kruskalM: structured two-resource cost profile, algorithm output, andTimeMimplementations.
Main results #
rel_edgePartitionFrom_iff_eqvGen,rel_edgePartition_iff_eqvGen: exactRelation.EqvGencharacterization of edge-induced partitions.numComponents_le_edgePartitionFrom_add_length,isAcyclicFrom_cons: component-count lower bound and inductive characterization of relative acyclicity.kruskalEdges_isSpanningForest,kruskalEdges_isMinSpanningForest,kruskalEdges_isMinSpanningTree: Kruskal's algorithm computes a minimum-weight spanning forest (and a minimum spanning tree when connected).perm_kruskalEdges_of_isMinSpanningForest_of_nodup_weights: uniqueness of the minimum spanning forest up to permutation when edge weights are pairwise distinct.realized_kruskal: for any realization ofDisjointSet.spec nwith a resource bound, existence of aCslib.DataStructureexecution of the Kruskal client that returnskruskalEdges eswithin that bound.kruskalM_forest,kruskalM_rep,kruskalM_comparisons_le,kruskalM_ufSteps_le,kruskalM_total_le: functional correctness, final union-find representation,m ⌈log₂ m⌉comparison bound, andm (2 ⌊log₂ n⌋ + 3)disjoint-set step bound forkruskalM.
References #
Weighted edges and edge-induced partitions #
Instances For
Endpoint pair (e.u, e.v) forgetting the weight, for interoperability with unweighted
DisjointSet.spanningForestEdges.
Instances For
Total weight of an edge list.
Instances For
Permuting an edge list preserves its total weight.
Deleting edges of nonnegative weight does not increase the total weight.
Partition obtained by merging the endpoints of every edge in es starting from p.
Instances For
Connected-component partition of Fin n induced by es.
Instances For
edgePartitionFrom p es is the final partition reached by
DisjointSet.spanningForestEdges WeightedEdge.endpoints es.
Exact equivalence-closure characterization of edgePartitionFrom p es.
Two vertices are in the same component of edgePartition es iff they are connected by the
equivalence closure of the edges in es.
Every edge in es relates its endpoints in edgePartitionFrom p es.
edgePartitionFrom is invariant under edge-list permutations.
edgePartition is invariant under edge-list permutations.
Merging (x, y) before folding es is equal to folding es and then merging (x, y).
Each edge reduces the number of connected components by at most 1.
Relative acyclicity, spanning forests, and spanning trees #
F is acyclic relative to an initial partition p when every edge in F merges two distinct
components, decreasing the component count by F.length.
Instances For
An edge list F on Fin n is acyclic (a forest) when each edge merges two distinct connected
components starting from Partition.discrete n, i.e.,
(edgePartition F).numComponents + F.length = n.
Instances For
Inductive characterization of relative acyclicity: e :: F is acyclic over p iff e's
endpoints lie in distinct classes of p and F is acyclic over p.merge e.u e.v.
Relative acyclicity is invariant under edge-list permutations.
Acyclicity is invariant under edge-list permutations.
Every acyclic edge list on Fin n has at most n edges (and at most n - 1 when 0 < n).
No self-loop belongs to an acyclic edge list.
Relative spanning forest of es starting from partition p.
Instances For
F is a spanning forest of the multigraph es when F is a sub-multiset (Subperm) of es,
F is acyclic, and F induces the exact same connected components as es.
Instances For
T is a spanning tree of es when T is a sub-multiset of es, T is acyclic, and T
connects Fin n into a single component (numComponents = 1).
Instances For
F is a minimum-weight spanning forest of es when F is a spanning forest of es of
minimum totalWeight.
Instances For
T is a minimum-weight spanning tree of es when T is a spanning tree of es of minimum
totalWeight.
Instances For
Every spanning tree on a connected multigraph (edgePartition es).numComponents = 1 is a
spanning forest, and vice versa.
Every spanning tree on Fin n has n - 1 edges (T.length + 1 = n).
All spanning forests of the same multigraph es have the exact same cardinality
n - (edgePartition es).numComponents.
Pure Kruskal scan, merge-sort, optimality, and uniqueness #
Greedy Kruskal scan starting from partition p: inspect edges in order, accepting an edge iff
its endpoints currently lie in distinct classes of p.
Instances For
kruskalScan p es is the edge list returned by
DisjointSet.spanningForestEdges WeightedEdge.endpoints es.
kruskalScan p es is an order-preserving sublist of es.
kruskalScan p es preserves the final partition edgePartitionFrom p es.
kruskalScan p es is acyclic relative to p.
kruskalScan p es is a relative spanning forest of es over p.
Core exchange-inductive optimality of kruskalScan on weight-sorted inputs: for any initial
partition p and any relative spanning forest H of sorted over p, kruskalScan p sorted has
total weight at most totalWeight H.
Uniqueness of the minimum relative spanning forest on weight-sorted inputs with distinct weights.
Sort an edge list by nondecreasing weight using List.mergeSort.
Instances For
Balanced-split unfolding step for sortEdges on lists of length at least 2.
sortEdges es is a permutation of es.
Over a linear order, sortEdges es is sorted in nondecreasing weight order.
Kruskal's algorithm: merge-sort edges in nondecreasing weight order and scan from the discrete
partition Partition.discrete n, retaining each edge whose endpoints lie in distinct components.
Instances For
kruskalEdges es is sorted in nondecreasing weight order.
kruskalEdges es is a sub-multiset (Subperm) of es.
kruskalEdges es is acyclic.
kruskalEdges es induces the exact same connected-component partition as es.
kruskalEdges es is a spanning forest of es.
Kruskal's minimum spanning forest theorem (generalizing Cormen et al., Section 23.2,
MST-KRUSKAL, p. 631 from connected simple real-weighted graphs to arbitrary finite
multigraphs): for any weighted multigraph es on Fin n with weights in an ordered additive
commutative monoid, kruskalEdges es is a minimum-weight spanning forest of es.
With nonnegative weights, Kruskal's forest weighs at most its whole input.
Kruskal's minimum spanning tree theorem (Cormen et al., Section 23.2, p. 631):
when es connects Fin n into a single component ((edgePartition es).numComponents = 1),
kruskalEdges es is a minimum-weight spanning tree of es.
Distinct-weight uniqueness of Kruskal's forest (generalizing Kruskal, Opening paragraph,
Construction A, and uniqueness proof, pp. 48–50): when all edges in es have pairwise
distinct weights, every minimum-weight spanning forest of es is a permutation of kruskalEdges es
(i.e., selects the exact same multiset of edges).
Distinct-weight uniqueness specialized to connected multigraphs: when edge weights in es are
pairwise distinct and es is connected, every minimum spanning tree of es is a permutation of
kruskalEdges es.
DisjointSet.spec client and backend-parametric realization #
DisjointSet.spec n client for the Kruskal scan, defined as the WeightedEdge.endpoints
specialization of DisjointSet.spanningForestEdges.
Instances For
eval view of kruskalClient.
Exact mathematical evaluation and operation history of kruskalClient: it computes
kruskalScan p es, reaches edgePartitionFrom p es, and issues one Op.union e.u e.v per edge.
Projecting endpoints of kruskalClient matches unweighted
DisjointSet.spanningForestEdges id.
Backend-parametric Kruskal execution: for any Cslib.DataStructure.StateTransitionImpl and
Cslib.DataStructure.Bound for DisjointSet.spec n, from a state representing
Partition.discrete n there is an execution of kruskalClient (sortEdges es) that returns
kruskalEdges es, ends in a state representing edgePartition es, and has total cost at most
the total charge of its m Op.union calls.
TimeM complexity with sorting comparisons and UnionFind steps #
Two-component cost profile for Kruskal's algorithm: edge-weight comparisons during merge sort and disjoint-set forest steps during the cycle-filtering scan.
- comparisons : ℕ
Number of edge-weight comparisons performed while sorting edges.
- ufSteps : ℕ
Number of disjoint-set forest steps (
findparent-pointer inspections plusunionroot links) performed during the Kruskal scan.
Instances For
Combined operation count comparisons + ufSteps.
Instances For
Concrete output of kruskalM: the selected forest edges together with the final
DisjointSet.UnionFind.State n representing the graph's connected components.
- forest : List (WeightedEdge n W)
Selected spanning-forest edges.
- ufState : DataStructure.DisjointSet.UnionFind.State n
Final concrete union-find forest state after scanning all edges.
Instances For
Merge-sort edges by nondecreasing weight in TimeM ℕ, charging 1 per weight comparison.
Instances For
sortEdgesM es performs at most es.length * Nat.clog 2 es.length weight comparisons.
Timed Kruskal scan starting from a concrete DisjointSet.UnionFind.State n, charging exact
union-find forest steps to Cost.ufSteps.
Instances For
Correctness and O(log n) per-edge disjoint-set step bound for kruskalScanM.
Kruskal's minimum-spanning-forest algorithm in TimeM Cost (Cormen et al., Section 23.2,
MST-KRUSKAL, pp. 631–633): merge-sort es by weight (charging comparisons to
Cost.comparisons) and scan the sorted edges from DisjointSet.UnionFind.init n (charging
post-initialization parent-pointer inspections and root links to Cost.ufSteps, with
DisjointSet.UnionFind.init n uncharged).
Instances For
Pure return value of kruskalM es in terms of kernel-reducible sortEdges and
kruskalScanM.
kruskalM es computes kruskalEdges es.
Over an ordered additive commutative monoid, (kruskalM es).ret.forest is a minimum-weight
spanning forest of es.
Final union-find state invariant: (kruskalM es).ret.ufState represents edgePartition es.
The maintained component counter of (kruskalM es).ret.ufState equals the number of connected
components (edgePartition es).numComponents.
Sorting comparison bound for kruskalM: at most m * ⌈log₂ m⌉ weight comparisons where
m = es.length.
Disjoint-set forest step bound for kruskalM: at most m * (2 * ⌊log₂ n⌋ + 3) union-find
steps where m = es.length.
Combined O(m log m + m log n) bound on total operations (kruskalM es).time.total.