|
| 1 | +" |
| 2 | +The Edmonds-Karp algorithm is an implementation of the Ford-Fulkerson method for computing the maximum flow in a flow network. It uses breadth-first search to find the shortest augmenting path from source to sink in terms of the number of edges. |
| 3 | +
|
| 4 | +The algorithm works by: |
| 5 | +1. Initialize flow to 0 for all edges |
| 6 | +2. While there exists an augmenting path from source to sink: |
| 7 | + a. Find the shortest augmenting path using BFS |
| 8 | + b. Find the minimum residual capacity along this path |
| 9 | + c. Update the flow along this path |
| 10 | +3. Return the maximum flow value |
| 11 | +
|
| 12 | +Time complexity: O(VE²) where V is the number of vertices and E is the number of edges. |
| 13 | +
|
| 14 | +Example usage: |
| 15 | +|nodes edges edmondsKarp maxFlow| |
| 16 | +nodes := #( $s $a $b $t ). |
| 17 | +edges := #( #( $s $a 10 ) #( $s $b 8 ) #( $a $b 5 ) #( $a $t 10 ) #( $b $t 10 ) ). |
| 18 | +
|
| 19 | +edmondsKarp := AIEdmondsKarp new. |
| 20 | +edmondsKarp nodes: nodes. |
| 21 | +edmondsKarp |
| 22 | + edges: edges |
| 23 | + from: [ :each | each first ] |
| 24 | + to: [ :each | each second ] |
| 25 | + capacity: [ :each | each third ]. |
| 26 | +
|
| 27 | +edmondsKarp source: $s sink: $t. |
| 28 | +maxFlow := edmondsKarp run. |
| 29 | +" |
| 30 | +Class { |
| 31 | + #name : 'AIEdmondsKarp', |
| 32 | + #superclass : 'AIGraphAlgorithm', |
| 33 | + #instVars : [ |
| 34 | + 'source', |
| 35 | + 'sink', |
| 36 | + 'parent' |
| 37 | + ], |
| 38 | + #category : 'AI-Algorithms-Graph-Maximum Flow', |
| 39 | + #package : 'AI-Algorithms-Graph', |
| 40 | + #tag : 'Maximum Flow' |
| 41 | +} |
| 42 | + |
| 43 | +{ #category : 'configuration' } |
| 44 | +AIEdmondsKarp >> edgeClass [ |
| 45 | + ^ AINetworkFlowEdge |
| 46 | +] |
| 47 | + |
| 48 | +{ #category : 'building - graph' } |
| 49 | +AIEdmondsKarp >> edges: aCollection from: source to: target capacity: capacityFunction [ |
| 50 | + | edge reverseEdge | |
| 51 | + aCollection do: [ :eModel | |
| 52 | + edge := self addEdge: eModel from: source to: target. |
| 53 | + edge ifNotNil: [ |
| 54 | + edge capacity: (capacityFunction value: eModel). |
| 55 | + edge flow: 0 ]. |
| 56 | + |
| 57 | + "Add reverse edge with 0 capacity for residual graph" |
| 58 | + reverseEdge := self addEdge: eModel from: target to: source. |
| 59 | + reverseEdge ifNotNil: [ |
| 60 | + reverseEdge capacity: 0. |
| 61 | + reverseEdge flow: 0 ] ] |
| 62 | +] |
| 63 | + |
| 64 | +{ #category : 'private' } |
| 65 | +AIEdmondsKarp >> findAugmentingPathBFS [ |
| 66 | + "Find an augmenting path from source to sink using BFS. |
| 67 | + Returns true if a path exists, false otherwise. |
| 68 | + Updates the parent array to reconstruct the path." |
| 69 | + |
| 70 | + | queue visited | |
| 71 | + queue := LinkedList new. |
| 72 | + visited := Set new. |
| 73 | + parent := Dictionary new. |
| 74 | + |
| 75 | + queue addLast: source. |
| 76 | + visited add: source. |
| 77 | + |
| 78 | + [ queue isNotEmpty ] whileTrue: [ |
| 79 | + | current | |
| 80 | + current := queue removeFirst. |
| 81 | + |
| 82 | + current = sink ifTrue: [ ^ true ]. |
| 83 | + |
| 84 | + current outgoingEdges do: [ :edge | |
| 85 | + | neighbor residualCapacity | |
| 86 | + neighbor := edge to. |
| 87 | + residualCapacity := edge capacity - edge flow. |
| 88 | + |
| 89 | + (visited includes: neighbor) not & (residualCapacity > 0) ifTrue: [ |
| 90 | + queue addLast: neighbor. |
| 91 | + visited add: neighbor. |
| 92 | + parent at: neighbor put: edge ] ] ]. |
| 93 | + |
| 94 | + ^ false |
| 95 | +] |
| 96 | + |
| 97 | +{ #category : 'private' } |
| 98 | +AIEdmondsKarp >> findBottleneckCapacity [ |
| 99 | + "Find the minimum residual capacity along the augmenting path" |
| 100 | + |
| 101 | + | current minCapacity | |
| 102 | + current := sink. |
| 103 | + minCapacity := Float infinity. |
| 104 | + |
| 105 | + [ current ~= source ] whileTrue: [ |
| 106 | + | edge residualCapacity | |
| 107 | + edge := parent at: current. |
| 108 | + residualCapacity := edge capacity - edge flow. |
| 109 | + minCapacity := minCapacity min: residualCapacity. |
| 110 | + current := edge from ]. |
| 111 | + |
| 112 | + ^ minCapacity |
| 113 | +] |
| 114 | + |
| 115 | +{ #category : 'private' } |
| 116 | +AIEdmondsKarp >> findReverseEdge: anEdge [ |
| 117 | + "Find the reverse edge for the given edge" |
| 118 | + |
| 119 | + ^ anEdge to outgoingEdges |
| 120 | + detect: [ :edge | edge to = anEdge from ] |
| 121 | + ifNone: [ nil ] |
| 122 | +] |
| 123 | + |
| 124 | +{ #category : 'initialization' } |
| 125 | +AIEdmondsKarp >> initialize [ |
| 126 | + super initialize. |
| 127 | + parent := Dictionary new |
| 128 | +] |
| 129 | + |
| 130 | +{ #category : 'configuration' } |
| 131 | +AIEdmondsKarp >> nodeClass [ |
| 132 | + ^ AIPathDistanceNode |
| 133 | +] |
| 134 | + |
| 135 | +{ #category : 'running' } |
| 136 | +AIEdmondsKarp >> run [ |
| 137 | + "Execute the Edmonds-Karp algorithm and return the maximum flow value" |
| 138 | + |
| 139 | + | maxFlow pathFlow | |
| 140 | + maxFlow := 0. |
| 141 | + |
| 142 | + [ self findAugmentingPathBFS ] whileTrue: [ |
| 143 | + pathFlow := self findBottleneckCapacity. |
| 144 | + self updateFlowAlongPath: pathFlow. |
| 145 | + maxFlow := maxFlow + pathFlow ]. |
| 146 | + |
| 147 | + ^ maxFlow |
| 148 | +] |
| 149 | + |
| 150 | +{ #category : 'accessing' } |
| 151 | +AIEdmondsKarp >> source: sourceNode sink: sinkNode [ |
| 152 | + source := self findNode: sourceNode. |
| 153 | + sink := self findNode: sinkNode |
| 154 | +] |
| 155 | + |
| 156 | +{ #category : 'private' } |
| 157 | +AIEdmondsKarp >> updateFlowAlongPath: flowValue [ |
| 158 | + "Update the flow along the augmenting path found by BFS" |
| 159 | + |
| 160 | + | current | |
| 161 | + current := sink. |
| 162 | + |
| 163 | + [ current ~= source ] whileTrue: [ |
| 164 | + | edge reverseEdge | |
| 165 | + edge := parent at: current. |
| 166 | + |
| 167 | + "Update forward edge flow" |
| 168 | + edge flow: edge flow + flowValue. |
| 169 | + |
| 170 | + "Update reverse edge flow" |
| 171 | + reverseEdge := self findReverseEdge: edge. |
| 172 | + reverseEdge flow: reverseEdge flow - flowValue. |
| 173 | + |
| 174 | + current := edge from ] |
| 175 | +] |
0 commit comments