W13. Minimum Spanning Trees
1. Theory
1.1 Greedy Algorithms
1.1.1 Optimization Problems
Many important algorithmic problems are optimization problems: there is a large set of feasible solutions, each with an associated cost or value, and the goal is to find one with minimum or maximum objective value. Such problems are recognisable by formulations using words like “shortest”, “minimum”, “maximum”, and “cheapest”.
This differs from a decision problem, where we only ask whether some feasible solution exists. For example, asking whether there is a path from
1.1.2 The Greedy Pattern
A greedy algorithm builds a solution incrementally and commits to a locally best-looking choice at each step. Greedy methods are attractive because they are usually simple and fast. However, greediness is not automatically correct: some problems admit no correct greedy strategy at all.
For minimum spanning trees, greedy algorithms are correct. The reason is structural: there is a theorem, the cut property, that identifies which local choices are always safe. Prim’s and Kruskal’s algorithms both work by repeatedly choosing such safe edges.
1.2 Minimum Spanning Trees
1.2.1 Spanning Trees and Their Weight
Let
If each edge has a real-valued weight, then the weight of a spanning tree is
A minimum spanning tree (MST) is a spanning tree whose total weight is as small as possible. If all edge weights are equal, then every spanning tree is automatically an MST, because all spanning trees have the same number of edges and therefore the same total weight.
If the graph is disconnected, a spanning tree does not exist. The right notion is then a spanning forest: a union of spanning trees, one for each connected component. A minimum spanning forest minimises the total weight over all such forests.
The historical origin of the MST problem is often traced to Otakar Borůvka’s 1926 work on designing a low-cost electrical network in Moravia: connect all required locations while using as little total cable length as possible.
1.2.2 Example: Finding an MST
Consider the following weighted undirected graph on six vertices:
One MST consists of edges
1.2.3 The Cut Property
The key theorem behind MST algorithms is the cut property. A cut
Theorem (Cut Property). Let
This theorem says that once we have a partial solution that could still extend to an MST, the cheapest edge crossing an appropriate cut is always safe to add. Prim’s and Kruskal’s algorithms differ in how they define the cut, but both repeatedly use this same principle.
1.3 Prim’s Algorithm
1.3.1 A Naive Growing Strategy
A natural first idea is to grow a tree from a chosen root by always taking the cheapest edge from the current tree to a vertex outside the tree. This strategy is conceptually close to Prim’s algorithm, but a direct implementation often stores edges in the priority queue, not vertices. That means the queue can contain many obsolete edges whose outside endpoint has already been absorbed into the tree.
The naive method is still correct for MST construction, but it is less elegant and often less efficient to implement than the standard formulation of Prim’s algorithm.
1.3.2 Prim’s Algorithm
Prim’s algorithm grows the MST from a root vertex
At every step:
- extract the non-tree vertex
with minimum key; - add
to the tree; - inspect every edge
leaving ; - if
is still outside the tree and is smaller than , update and set .
Thus the priority queue stores vertices, not edges. Each vertex is present only once in the queue, and its priority is decreased whenever we discover a cheaper connection to the current tree.
1.3.3 Pseudocode
MST-PRIM(G, w, r)
1 for each vertex u ∈ G.V
2 u.key = ∞
3 u.π = NIL
4 r.key = 0
5 Q = ∅
6 for each vertex u ∈ G.V
7 INSERT(Q, u)
8 while Q ≠ ∅
9 u = EXTRACT-MIN(Q)
10 for each vertex v in G.Adj[u]
11 if v ∈ Q and w(u, v) < v.key
12 v.π = u
13 v.key = w(u, v)
14 DECREASE-KEY(Q, v, w(u, v))
After the algorithm terminates, the MST consists of all edges
1.3.4 Why Prim’s Algorithm is Correct
At the moment a vertex
Therefore each iteration preserves the invariant that the chosen edges remain extendable to an MST. After
1.3.5 Complexity Analysis
The total running time depends on the priority-queue implementation.
- Initialization of all keys and predecessors costs
. EXTRACT-MINis called once per vertex.DECREASE-KEYis called whenever an edge gives a better connection to a vertex still outside the tree.
With a binary min-heap:
extractions cost ;- up to
successful key decreases cost .
So the total running time is
With a Fibonacci heap, DECREASE-KEY is
An additional useful bound appears in worst-case traces. Let DECREASE-KEY operations can be
Why? The second extracted vertex can be improved once before it is chosen, the third can be improved twice, and in general the vertex extracted in position
1.4 Disjoint-Set Data Structures
1.4.1 Motivation and Operations
Many applications maintain a family of pairwise disjoint sets and repeatedly ask two kinds of questions:
- which set contains a given element?
- how do we merge two sets into one?
The disjoint-set data structure (also called union-find or DSU) supports exactly this with three operations:
MAKE-SET(x)creates a singleton set ;FIND-SET(x)returns the representative of the set containing ;UNION(x, y)merges the sets containing and .
The representative is simply an element chosen to identify the set. The crucial invariant is that all elements in the same set must return the same representative, so equality of representatives becomes a constant-time test for membership in the same component.
1.4.2 Linked-List Representation
In the simplest representation, each set is a linked list. The head of the list is the representative, and every node stores a pointer to that head.
MAKE-SETis .FIND-SETis because the representative pointer is stored directly.UNIONmay be expensive because if one list is appended to another, the representative pointer of every node in the appended list must be updated.
In the worst case, one UNION can therefore cost
1.4.3 Weighted Union (Union by Size)
To improve linked-list DSU, store the size of each list and always append the shorter list to the longer one. Then only the smaller side needs representative-pointer updates.
This yields a standard amortized bound: each element’s representative pointer is updated at most
As a result, linked lists with union by size support UNION in FIND-SET at
1.4.4 Forest Representation
A more powerful representation stores each set as a rooted tree. The root is the representative, and every node stores only a parent pointer. A root points to itself.
MAKE-SET(x): create a one-node tree withparent[x] = x.FIND-SET(x): follow parent pointers to the root.UNION(x, y): link one root under the other.
Without balancing, repeated unions may produce a long chain of height FIND-SET very slow.
1.4.5 Union by Rank
To keep forest trees shallow, store a rank at each root. When merging two sets:
- if the ranks differ, attach the lower-rank root under the higher-rank root;
- if the ranks are equal, choose one root as the new root and increment its rank by 1.
The core theorem is that a root of rank
Hence union by rank alone gives
1.4.6 Path Compression
Path compression speeds up FIND-SET(x) by making every node on the search path point directly to the root.
The effect is dramatic in long operation sequences: once a path has been compressed, future finds on those nodes become much cheaper. Path compression does not recompute ranks exactly; after compression, ranks remain safe upper bounds rather than exact heights.
1.4.7 Combined Complexity and Practical Augmentation
With union by rank together with path compression, the amortized complexity of both FIND-SET and UNION is
where
An important practical idea is that DSU can be augmented with aggregate information stored only at representatives. For example, if each set must answer:
- the total weight or sum of values in the component;
- the number of elements in the component;
- the average value of elements in the component;
then each representative can store fields such as
sum[root];size[root].
When two sets are merged, the new root updates these in
sum[newRoot] = sum[root1] + sum[root2];
size[newRoot] = size[root1] + size[root2];Then the average for the component containing
The asymptotic complexity does not change, because the extra bookkeeping is constant-time per merge.
1.4.8 Comparison of Main DSU Variants
| Representation | MAKE-SET |
FIND-SET |
UNION |
|---|---|---|---|
| Lists (naive union) | |||
| Lists (union by size) | |||
| Forest (naive) | |||
| Forest (union by rank) | |||
| Forest (rank + path compression) |
For algorithm design and implementation, the final row is almost always the best choice.
1.5 Kruskal’s Algorithm
1.5.1 Idea
Kruskal’s algorithm grows the MST in the opposite direction from Prim’s algorithm. Instead of expanding one tree from a root, it starts with
The algorithm is:
- create one singleton set for each vertex;
- sort all edges by non-decreasing weight;
- process edges in that order;
- add an edge
if and only ifFIND-SET(u) \ne FIND-SET(v).
If the edge’s endpoints already lie in the same DSU component, adding the edge would create a cycle, so the edge is skipped.
1.5.2 Why Kruskal’s Algorithm is Correct
At any moment, the edges already accepted by Kruskal form a forest. Consider the next accepted edge
Repeating this argument shows that every accepted edge preserves the possibility of extending to an MST. Once
1.5.3 Running Time
The running time has two parts:
- sorting the edges;
- DSU operations during the scan.
Sorting costs
because in a simple graph
The DSU part performs
In general, sorting dominates, so the overall complexity is
If the edges are already sorted, or can be sorted in linear time, then the DSU term dominates and the bound improves to
Tie-breaking matters for execution traces: if equal-weight edges exist, the order of accepted edges and the exact final DSU parent structure need not be unique. However, every valid run still produces a minimum spanning tree (or forest) of the same total weight.
1.6 The Ackermann Function and Its Inverse
1.6.1 Ackermann Function
The Ackermann function is defined recursively by
where
This family grows extraordinarily quickly:
and
1.6.2 Inverse Ackermann Function
The inverse Ackermann function is
Using the values above:
Because
2. Definitions
- Optimization problem: a problem in which the goal is to choose a feasible solution of minimum or maximum value.
- Greedy algorithm: an algorithm that builds a solution step by step by making a locally optimal-looking choice at each stage.
- Spanning tree: for a connected undirected graph
, a connected acyclic edge set containing all vertices. - Minimum spanning tree (MST): a spanning tree whose total edge weight is minimum among all spanning trees of the graph.
- Spanning forest: a maximal acyclic subgraph of a disconnected graph; equivalently, a union of spanning trees, one per connected component.
- Weight of a spanning tree: the sum
of the weights of its edges. - Cut
: a partition of the vertex set into two non-empty disjoint subsets. - Light edge: a minimum-weight edge crossing a given cut.
- Cut property: the theorem stating that a light edge crossing a cut that respects the current partial solution is safe to add to some MST.
- Prim’s algorithm: a greedy MST algorithm that grows one tree from a root by repeatedly extracting the non-tree vertex with smallest connection key.
- Key (in Prim’s algorithm): the weight of the currently cheapest known edge connecting a non-tree vertex to the current tree.
- Predecessor
: the vertex that currently gives its best known connection into the MST. - Disjoint-set data structure (DSU / union-find): a data structure that maintains a partition of elements into disjoint sets and supports
MAKE-SET,FIND-SET, andUNION. - Representative: the designated element or root used to identify a DSU set.
- Union by size: a linked-list DSU heuristic that appends the shorter list to the longer list.
- Forest representation: a DSU representation in which each set is a rooted tree and the root is the representative.
- Rank: an upper bound on subtree height stored at DSU roots and used by union by rank.
- Union by rank: the heuristic that attaches the lower-rank root under the higher-rank root, incrementing rank only when the two ranks are equal.
- Path compression: the optimization that makes every node on a
FIND-SETpath point directly to the root. - Representative metadata: aggregate information stored only at the root of a DSU set, such as component size or component sum.
- Kruskal’s algorithm: a greedy MST algorithm that scans edges in non-decreasing weight order and adds an edge exactly when it connects two different DSU components.
- Ackermann function
: a rapidly growing recursively defined function given by and for . - Inverse Ackermann function
: the minimum such that .
3. Formulas
- MST weight:
- Prim’s algorithm with binary heap:
- Prim’s algorithm with Fibonacci heap:
- Worst-case successful
DECREASE-KEYcalls in Prim on vertices: - Weighted-union pointer-update bound: each element’s representative pointer changes at most
times - Union-by-rank size lower bound: a root of rank
has at least nodes - Union-by-rank height bound:
- Component average in an augmented DSU:
- Kruskal’s algorithm, general case:
- Kruskal’s algorithm with linear-time sorting:
- Ackermann base case:
- Ackermann recursion:
for - Level-1 Ackermann closed form:
- Level-2 Ackermann closed form:
- Inverse Ackermann function:
4. Practice
4.1. Maximize DECREASE-KEY Calls in Prim’s Algorithm (Problem Set 12, Task 1)
Consider Prim’s algorithm on a connected undirected graph with source vertex
(a) Determine the worst-case number of successful DECREASE-KEY operations on an
(b) Exhibit a concrete 5-vertex graph achieving that bound.
(c) Run Prim’s algorithm on that graph using a binary min-heap and report the heap after each step.
Click to see the solution
Key Concept: In the worst case, a vertex can receive a new smaller key once for each earlier extracted vertex, so the total number of successful updates forms a triangular sum.
Let
(a) Worst-case count.
The source is extracted first and never receives a decrease. Now focus on the remaining vertices in the order they are eventually extracted. The second extracted vertex can have its key decreased at most once before leaving the queue, the third can have its key decreased at most twice, and in general the vertex extracted in position
Therefore the total worst-case number of successful DECREASE-KEY operations is
This bound is achievable by a carefully weighted complete graph in which every newly added vertex improves the key of every vertex still in the queue.
(b) A concrete 5-vertex construction.
Take the complete graph on vertices
These weights force Prim’s algorithm to extract vertices in the order
and each extracted vertex improves the key of every later vertex.
(c) Binary-heap trace.
We use a standard binary min-heap, and we list the heap as an array of (key, vertex) pairs after processing each extracted vertex.
Initial heap:
| Step | Vertex added | Edge used | Heap after processing | Successful DECREASE-KEY calls |
|---|---|---|---|---|
| 1 | source | [(10,A), (20,B), (30,C), (40,D)] |
4 | |
| 2 | [(9,B), (19,C), (29,D)] |
3 | ||
| 3 | [(8,C), (18,D)] |
2 | ||
| 4 | [(7,D)] |
1 | ||
| 5 | [] |
0 |
The total is
which matches the general formula.
Answer: The worst-case number of successful DECREASE-KEY calls is
4.2. Augment DSU to Answer Pool Average Queries (Problem Set 12, Task 2)
A CDN operator maintains caches
- merging the pools containing
and ; - checking whether
and are in the same pool; - reporting the average stored-object count in the pool containing
.
Design a DSU variant that supports these operations efficiently.
Click to see the solution
Key Concept: Store aggregate data only at representatives, and update that metadata in constant extra time whenever two sets are merged.
The right idea is to use the standard forest-based DSU with union by rank and path compression, and to store aggregate information only at representatives.
Data stored.
For each root
sum[r]: total stored-object count in the pool;size[r]: number of caches in the pool.
Initially, for every cache
Merge operation.
To merge the pools containing
- compute
and ; - if
, do nothing; - otherwise perform union by rank;
- if the new root is
, update
All non-root nodes may keep stale sum and size values, because queries always go through the representative.
Same-pool query.
Return
Average query.
Let
Pseudocode.
MERGE(a, b):
ra = FIND-SET(a)
rb = FIND-SET(b)
if ra == rb:
return
r = UNION(ra, rb) // returns the new representative
if r == ra:
sum[ra] = sum[ra] + sum[rb]
size[ra] = size[ra] + size[rb]
else:
sum[rb] = sum[ra] + sum[rb]
size[rb] = size[ra] + size[rb]
SAME-POOL(a, b):
return FIND-SET(a) == FIND-SET(b)
AVERAGE(a):
r = FIND-SET(a)
return sum[r] / size[r]
Complexity.
With union by rank and path compression:
MERGEperforms two finds, one union, and extra metadata updates;SAME-POOLperforms two finds;AVERAGEperforms one find and one division.
So each operation costs
and a sequence of
Answer: Use a rank-and-compression DSU augmented with sum[root] and size[root]; then MERGE, SAME-POOL, and AVERAGE all run in
4.3. Run Kruskal’s Algorithm and Inspect the Final DSU State (Problem Set 12, Task 3)
Consider the following weighted undirected graph on vertices
Assume Kruskal uses a forest-based DSU with union by rank and path compression. Break weight ties lexicographically by the ordered pair of endpoint labels.
Click to see the solution
Key Concept: Kruskal sorts edges by weight and accepts exactly those edges whose endpoints are in different DSU components; skipped edges are precisely the ones that would create cycles.
Step 1: Sort edges by weight.
The beginning of the sorted list is:
Step 2: Scan edges in order and accept only those joining different components.
| Edge | Weight | Action | Reason |
|---|---|---|---|
| 1 | add | different components | |
| 2 | add | different components | |
| 3 | add | joins |
|
| 3 | add | different components | |
| 3 | add | different components | |
| 4 | skip | ||
| 4 | add | different components | |
| 5 | add | joins |
|
| 5 | add | different components | |
| 7 | add | joins |
|
| 7 | add | joins |
|
| 8 | skip | endpoints already in the same component | |
| 8 | skip | endpoints already in the same component | |
| 8 | skip | endpoints already in the same component | |
| 8 | skip | endpoints already in the same component | |
| 9 | skip | endpoints already in the same component | |
| 9 | skip | endpoints already in the same component | |
| 9 | skip | endpoints already in the same component | |
| 9 | add | joins the left 8-vertex component with |
After
Its total weight is
Final DSU state.
Using the stated tie-breaking rule and choosing the root of the first endpoint’s set when ranks are equal, one valid final state after a full round of path compression is:
| Vertex | Parent | Rank |
|---|---|---|
| A | A | 3 |
| B | A | 0 |
| C | A | 1 |
| D | A | 0 |
| E | A | 2 |
| F | A | 0 |
| G | A | 1 |
| H | A | 0 |
| I | A | 2 |
| J | A | 0 |
| K | A | 1 |
| L | A | 0 |
All parents become
Are these answers unique?
Not completely. The set of accepted edges is forced here by the tie-breaking rule, but in general Kruskal traces can vary when equal-weight edges are processed in a different order. Likewise, the exact DSU parent and rank arrays are not unique because equal-rank unions may choose different roots. What remains invariant is the existence of a minimum spanning tree with the same total weight.
Answer: The accepted edges are
4.4. Trace Prim’s Algorithm on a Weighted Graph (Lecture 11, Example 1)
Apply both the naive edge-based heuristic and Prim’s algorithm to the graph from Section 1.2.2, starting from vertex
Click to see the solution
Key Concept: The naive method keeps frontier edges in the queue, while Prim keeps one queue entry per frontier vertex; both find the same MST, but Prim avoids obsolete edge entries.
We use the 6-vertex graph from Section 1.2.2, whose MST weight is 28.
Naive edge-based heuristic.
The priority queue stores frontier edges.
| Step | Action | Queue after the step |
|---|---|---|
| Start | Tree contains only |
|
| 1 | Extract |
|
| 2 | Extract |
|
| 3 | Extract |
|
| 4 | Extract |
|
| 5 | Extract |
done |
The resulting tree edges are
with total weight
Prim’s algorithm.
Now the priority queue stores vertices keyed by their cheapest known connection to the tree.
| Step | Extracted vertex | Edge added | Key updates |
|---|---|---|---|
| 1 | source | ||
| 2 | |||
| 3 | |||
| 4 | |||
| 5 | |||
| 6 | none |
So Prim produces the same MST:
again with total weight
The key implementation improvement is that every vertex appears only once in the queue. Old candidate edges do not accumulate, so the algorithm is cleaner and easier to analyze.
Answer: Both traces produce the MST
4.5. Apply the Naive MST Heuristic Starting from Vertex A (Lecture 11, Example 2)
Apply the naive “smallest edge from the current tree to the outside” heuristic to the graph from Section 1.2.2, starting from
Click to see the solution
Key Concept: The heuristic repeatedly extracts the cheapest edge leaving the current tree, so the only required data structure is a min-priority queue over frontier edges.
This is the same trace as in Example 4.4, but written from the data-structure point of view.
Initial queue:
Step 1. Extract
Step 2. Extract
Step 3. Extract
Step 4. Extract
Step 5. Extract
So the algorithm returns the MST
with total weight
The required data structure is a min-priority queue of edges, typically implemented as a binary min-heap. It supports repeated extraction of the minimum-weight candidate edge in
Answer: Starting from
4.6. Apply Prim’s Algorithm Starting from Vertex L (Lecture 11, Example 3)
Consider the undirected weighted graph on vertices
Run Prim’s algorithm from source vertex
Click to see the solution
Key Concept: Prim’s algorithm repeatedly finalizes the smallest current key, and every key decrease records a cheaper connection from the current tree to an outside vertex.
We maintain a min-priority queue of vertices keyed by their current best connection to the growing tree.
| Step | Extracted vertex | Edge added | Key | Newly improved vertices |
|---|---|---|---|---|
| 1 | source | 0 | ||
| 2 | 4 | |||
| 3 | 1 | |||
| 4 | 2 | none | ||
| 5 | 3 | |||
| 6 | 1 | |||
| 7 | 3 | |||
| 8 | 3 | |||
| 9 | 2 | |||
| 10 | 1 | none | ||
| 11 | 2 | |||
| 12 | 1 | |||
| 13 | 2 | |||
| 14 | 2 | |||
| 15 | 1 | none | ||
| 16 | 2 | none | ||
| 17 | 3 | none | ||
| 18 | 5 | none |
Therefore the Prim extraction order is
The MST edges chosen are
Their total weight is
Answer: The extraction order is
4.7. Prove Height Bound with Union by Rank (Lecture 11, Example 4)
Prove that if only union by rank is used, and path compression is not used, then every DSU tree on at most
Click to see the solution
Key Concept: Under union by rank, rank can grow only when two equally ranked trees are merged, which forces the subtree size to at least double.
The key statement is:
Claim. A root of rank
We prove this by induction on
Base case MAKE-SET, so it has exactly one node, and
Inductive step. Assume every rank-
nodes.
Thus the claim holds for all
Now let a tree have root rank
so
Because
Without path compression, the rank is an upper bound on the tree height, so
Consequently, both FIND-SET and UNION run in
Answer: Any DSU tree built using only union by rank has height at most FIND-SET and UNION are
4.8. Find Connected Components Using DSU (Lecture 11, Example 5)
Design an algorithm using DSU to compute the number of connected components of an undirected graph
Click to see the solution
Key Concept: DSU merges the endpoints of every edge, so after all unions, each representative corresponds exactly to one connected component.
The algorithm mirrors the definition of connected components.
CONNECTED-COMPONENTS(G):
for each vertex v in G.V:
MAKE-SET(v)
for each edge (u, v) in G.E:
if FIND-SET(u) != FIND-SET(v):
UNION(u, v)
count = 0
for each vertex v in G.V:
if FIND-SET(v) == v:
count = count + 1
return count
Why it works.
Whenever an edge
Time complexity.
MAKE-SETis called times: .- The edge scan performs
finds and unions: amortized. - The final root count performs
finds: .
So the total complexity is
How to output component sizes.
Store an additional field size[root], initialized to 1 for each singleton set. Whenever two components are united, update the size of the new root by adding the two old sizes. At the end, each root stores the size of its connected component.
Answer: Process all edges with DSU unions, then count distinct roots; this yields size[root] additionally gives each component size.
4.9. Apply Kruskal’s Algorithm to the 18-Vertex Graph (Lecture 11, Example 6)
Apply Kruskal’s algorithm to the 18-vertex graph from Example 4.6. Break ties lexicographically by endpoint labels.
Click to see the solution
Key Concept: Kruskal’s algorithm is a cut-property-driven greedy scan: the next accepted edge is always the lightest edge that connects two different current components.
We process the edges in non-decreasing order of weight and add an edge exactly when it connects two different DSU components.
| Edge | Weight | Action | Reason |
|---|---|---|---|
| 1 | add | different components | |
| 1 | add | different components | |
| 1 | add | different components | |
| 1 | add | different components | |
| 1 | add | different components | |
| 2 | add | joins |
|
| 2 | add | adds |
|
| 2 | add | adds |
|
| 2 | add | adds |
|
| 2 | add | adds |
|
| 2 | add | joins the |
|
| 3 | add | adds |
|
| 3 | add | joins the |
|
| 3 | add | joins the left and right large components | |
| 3 | skip | endpoints already connected | |
| 3 | skip | endpoints already connected | |
| 3 | add | adds |
|
| 4 | skip | endpoints already connected | |
| 4 | skip | endpoints already connected | |
| 4 | skip | endpoints already connected | |
| 4 | add | adds |
|
| 4 | skip | endpoints already connected | |
| 5 | add | adds |
At this point all 18 vertices belong to one component, so the MST is complete. The accepted edges are
Their total weight is
This illustrates the main logic of Kruskal’s algorithm: equal-weight edges may appear in different orders, but every accepted edge must connect two currently different components, and every skipped edge would form a cycle.
Answer: With lexicographic tie-breaking, Kruskal accepts
4.10. Compute Ackermann Function Values (Lecture 11, Example 7)
Compute
Click to see the solution
Key Concept: Each Ackermann level is defined by iterating the previous level many times, so even the first few values grow extremely quickly.
We use the definition
For
For
For
Since
For
Now
Hence
For
This means applying
So the key values are:
| 0 | 2 |
| 1 | 3 |
| 2 | 7 |
| 3 | 2047 |
| 4 | unimaginably large |
Therefore:
This is why the inverse Ackermann function is treated as essentially constant in real implementations.
Answer: The values are