Proofs: Eppstein's k shortest paths
These are the human proofs of Eppstein's k shortest paths, at full length.
Eppstein's algorithm lists the k lightest walks full
This proof shows: correctness, termination.
Claim. Let have real weights, and let be vertices and . If a negative cycle lies on some - walk, EPPSTEIN reports it. Otherwise it ends and outputs different - walks, where is the number of - walks, in nondecreasing order of weight, each with its correct weight, such that every - walk not output weighs at least as much as the last walk output.
Walks are sequences of edges, so parallel edges give different walks.
Step 1: the restriction. Let be the set of vertices that are reachable from and reach . Every vertex of an - walk is in , since the prefix of the walk reaches it from and the suffix leads from it to . So the - walks of and of are the same. Conversely, every cycle in lies on an - walk: go from to a vertex of , around , and on to . So has a negative cycle exactly when a negative cycle lies on an - walk. Dijkstra's algorithm (when all weights in are nonnegative, so no negative cycle exists) or Bellman-Ford on the reversed graph finds the distances from each to or reports a negative cycle of , and the report is correct by the correctness of those algorithms. If is not reachable from , then , there is no - walk, and the empty output is correct. From now on assume and no negative cycle in ; then is finite for all .
Step 2: the tree. For , let be the vertex that last lowered , and the tree edge the edge used. At the end, , and the tree edges form a tree into : a cycle among them would be created by a strict decrease of a label along it, which forces a negative cycle (the standard predecessor-graph argument for relaxation algorithms). So from every , following tree edges reaches after finitely many steps; write for this tree path, and note . Here is empty.
Step 3: sidetracks. Every edge of that is not a tree edge is a sidetrack, with . Since for a distance function, . Tree edges have .
For a walk from to , let be the sequence of its non-tree edges, in order. Call a sequence of sidetracks valid if the tail of is on , and the tail of each is on .
(a) is a bijection from - walks to valid sequences. Between two consecutive sidetracks, and before the first and after the last, a walk uses only tree edges, and from any vertex there is exactly one tree edge; so the tree part from a vertex is a prefix of . Hence is valid, the last stretch is all of (it must end at , and visits only at its end), and is determined by . Conversely, a valid sequence gives the walk up to the tail of , then , then up to the tail of , and so on, ending with ; its sequence of non-tree edges is .
(b) . Write for each edge of . The terms telescope to , and the reduced costs are 0 on tree edges and on sidetracks.
Step 4: the heaps. For , the heap has the sidetracks out of , the cheapest at the root and the rest heap-ordered below it. is built from of nothing by inserting the root of (if any), and for , is with the root of inserted (if any). Line L3 handles after , so this is well defined, and since the insertion is persistent, is not changed. By induction along , is a heap-ordered tree whose nodes are the roots of for the vertices of (including and ) with nonempty . Hang below its root in . The result, call it , is a tree that contains every sidetrack whose tail lies on exactly once, and it is heap-ordered: the children in have keys at least their parent's by the heap property, and the rest of has keys at least its root's. Every node of has at most four children (two in the leftist heap, two in the array heap).
Step 5: states and walks. A state of the search is where is a node of some . Define the parent of a state created at line L9 to be the state removed in that iteration, and likewise at line L10. We show by induction over the order of creation that each state satisfies:
(i) is a valid sequence, and is a node of where if is empty and of the last sidetrack of otherwise;
(ii) .
For the state of line L5, is the root of and is empty, so both hold. A state made at line L9 from has the same and a child of in the same tree , so (i) holds by Step 4, and its cost satisfies (ii). A state made at line L10 has the sequence and the root of , whose sidetrack has its tail on , so (i) holds, and (ii) holds by adding of the root.
Now let be the set of all states that the rules can ever create (the tree of states under the parent relation, rooted at the state of line L5). The map is a bijection from to the nonempty valid sequences. Onto: given a valid sequence , is at a unique node of , reached from the root by a unique sequence of L9 moves; one L10 move goes to the root of , where is at a unique node, and so on. One-to-one: a state's history of moves can be read back from it: the number of L10 moves is the length of , the sidetracks of name the nodes where those moves were made, and inside each tree the path from the root to a node is unique. So two different move histories give two different pairs .
By Step 3 and (ii), the cost of a state is the weight of the walk with the sidetrack sequence of the state. The tree walk is the walk with the empty sequence, of weight , output at line L4.
Step 6: order of output. All moves have nonnegative cost: an L9 move costs by heap order, and an L10 move costs by Step 3. So a state costs at least as much as its parent. The queue holds, at the start of each iteration of line L6, exactly the states of not yet removed whose parent has been removed (and the root, before the first removal): this holds at line L5, and an iteration removes one state and inserts all its children (at most five), none of which has been seen before since each state has one parent. Every state of not yet removed has an ancestor in (follow the parent links up to the first one not removed), whose cost is at most its own. Hence the state removed at line L7 costs at most every state not yet removed, and the costs removed form a nondecreasing sequence; each state is removed at most once.
Step 7: conclusion. The output is the tree walk followed by the walks of the removed states, in order. By Step 5 these are different walks, with their correct weights. The tree walk weighs , which is at most the weight of every walk by Step 3(b). By Step 6 the weights are nondecreasing, and when the loop stops with walks output, every walk not output is the walk of a state not removed, which costs at least the last cost removed. If the loop stops because is empty, every state of was removed, so every - walk was output, and is finite.
Termination. If , is finite and every iteration removes one state, so the loop ends. Otherwise it ends after iterations. Lines L1 to L4 are finite.