Skip to content

Commit 6150540

Browse files
authored
Merge pull request #11 from Driolar/master
Revised chapters 3,4,5,7 and 8.
2 parents ba9d01c + b2b6890 commit 6150540

File tree

7 files changed

+535
-173
lines changed

7 files changed

+535
-173
lines changed

Chapters/Chapter3/chapter3.md

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ Besides viewing the graph, we can interact with it. Thanks to Roassal's canvas c
198198

199199
- inspect the model of a selected node or edge
200200

201-
- rearrange the position of the nodes
201+
- rearrange the position of a node
202202

203203
- zoom in and out.
204204

@@ -225,7 +225,29 @@ structure nodes: (kruskal nodes collect: [:node | node model ]).
225225
structure inspect
226226
```
227227

228-
Note that we can also use `AIGraphNonWeightedFixtureStructure` for a weighted graph in case we do not want the edge weights to be drawn.
228+
![Visualized weighted graph with edge labels.](figures/edgelabels.png width=80&label=edgelabels)
229+
230+
In case of visualizing a weighted graph, each edge weight is rendered in the inner middle of the edge arrow like in Figure *@edgelabels@*. You might want to move the edge labels for better readability, e.g. if you need the graph for a presentation.
231+
In such a case, you can export it to SVG like in this example:
232+
233+
```
234+
RSSVGCairoExporter new
235+
canvas: AIWeightedDAGFixture new weightedDAG buildGraphCanvas;
236+
exportToFile: 'c:/temp/foo.svg' asFileReference
237+
```
238+
239+
Then you can keep working on it with your favorite SVG editor.
240+
241+
`RSSVGCairoExporter` and other Roassal exporters can be loaded by executing this code snippet:
242+
243+
```
244+
Metacello new
245+
baseline: 'RoassalExporters';
246+
repository: 'github://pharo-graphics/RoassalExporters';
247+
load.
248+
```
249+
250+
Furthermore, note that we can also use `AIGraphNonWeightedFixtureStructure` for a weighted graph in case we do not want the edge weights to be drawn.
229251

230252
### Conclusion
231253

182 KB
Loading

Chapters/Chapter4/chapter4.md

Lines changed: 156 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,32 @@
1-
## Topological sortingLet us start with our first algorithm: a topological sort.A topological sort makes that a node is treated before the nodes that depend on it are treated.If you consider tasks, it means that you want to do first a task before doing the ones that depend on this one.Topological sorting is a way of ordering a **directed acyclic graph** such that for every directed edge $(U, V)$ fromnode $U$ to node $V$, $U$ becomes first in the resulting ordering.We present here one algorithm named Khan's algorithm.![A graph and one of its topological sorts.](figures/top_sort_visual_example.pdf width=90&label=soft)### ExampleThe topological sorting can be applied to a graph in which its nodes represents software dependencies.For example to install a library, there are some modules that need to be installed before others.So, in this case, a topological sort is useful to know which modules to install first \(the one that has no dependencies\) and so on.Now for a same graph multiple topological sorts are possible just because since we treat first the nodes having no dependenciestheir traversal order can be different and in addition the traversal order of their dependent can be different too. Figure *@soft@* shows one graph and one of the possible topological sort.### Kahn's algorithmTo apply a topological sort to a graph, the graph must be a directed acyclic graph \(DAG\). There is at least one topologicalpossible order for a DAG.The algorithm that is used in this library is the Kahn's algorithm.It has a time complexity of $O(V + E)$.The pseudocode taken from [https://en.wikipedia.org/wiki/Topological\_sorting](https://en.wikipedia.org/wiki/Topological_sorting) is the following one:```L ← Empty list that will contain the sorted elements
1+
## Topological Sorting
2+
3+
Let us start with our first algorithm: a topological sort. A topological sort makes that a node is treated before the nodes that depend on it are treated. If you consider tasks, it means that you want to do first a task before doing the ones that depend on this one.
4+
5+
Topological sorting is a way of ordering a **directed acyclic graph** such that for every directed edge $(U, V)$ from node $U$ to node $V$, $U$ becomes first in the resulting ordering.
6+
7+
We present here one algorithm named Khan's algorithm.
8+
9+
![A graph and one of its topological sorts.](figures/top_sort_visual_example.pdf width=90&label=sort)
10+
11+
### Example
12+
13+
The topological sorting can be applied to a graph in which its nodes represents software dependencies.
14+
For example to install a library, there are some modules that need to be installed before others.
15+
So, in this case, a topological sort is useful to know which modules to install first \(the one that has no dependencies\) and so on.
16+
17+
Now for a same graph multiple topological sorts are possible just because since we treat first the nodes having no dependencies
18+
their traversal order can be different and in addition the traversal order of their dependent can be different too. Figure *@sort@* shows one graph and one of the possible topological sort.
19+
20+
### Kahn's algorithm
21+
22+
To apply a topological sort to a graph, the graph must be a directed acyclic graph \(DAG\). There is at least one topological possible order for a DAG.
23+
24+
The algorithm that is used in this library is the Kahn's algorithm.
25+
It has a time complexity of $O(V + E)$.
26+
The pseudocode{!footnote|taken from en.wikipedia.org\slash wiki\slash Topological\_sorting!} is the following one:
27+
28+
```
29+
L ← Empty list that will contain the sorted elements
230
S ← Set of all nodes with no incoming edge
331
while S is not empty do
432
remove a node n from S
@@ -10,75 +38,153 @@ while S is not empty do
1038
if graph has edges then
1139
return error (graph is not a DAG)
1240
else
13-
return L (a topologically sorted order)```This algorithm is implemented in the class `AITopologicalSorting` subclass of `AIGraphAlgorithm`.The parent class \(`AIGraphAlgorithm`\) provides all the mechanisms to handle the implementation of thegraph data structure. `AITopologicalSorting` has the only responsibility: to implement the logic of the algorithm.The following proposes a first implementation```AITopologicalSorting >> run
41+
return L (a topologically sorted order)
42+
```
43+
44+
This algorithm is implemented in the class `AITopologicalSorting` subclass of `AIGraphAlgorithm`.
45+
The parent class \(`AIGraphAlgorithm`\) provides all the mechanisms to handle the implementation of the
46+
graph data structure. `AITopologicalSorting` has the only responsibility: to implement the logic of the algorithm.
47+
48+
The following proposes a first implementation
49+
50+
```
51+
AITopologicalSorting >> run
1452
1553
topologicalSortedElements := OrderedCollection empty.
1654
nodesWithNoIncomingEdges := LinkedList empty.
1755
1856
"Obtain all the nodes without incoming nodes"
1957
nodesWithNoIncomingEdges addAll:
20-
(nodes select: [ :node | node incomingNodes isEmpty ]).
58+
(nodes select: [ :node | node incomingNodes isEmpty ]).
2159
2260
[ nodesWithNoIncomingEdges isNotEmpty ] whileTrue: [
23-
| node |
24-
node := nodesWithNoIncomingEdges removeFirst.
25-
topologicalSortedElements addLast: node model.
61+
| node |
62+
node := nodesWithNoIncomingEdges removeFirst.
63+
topologicalSortedElements addLast: node model.
2664
27-
"Remove all the edges of node from the graph"
28-
node adjacentNodes do: [ :adjacentNode |
29-
adjacentNode incomingNodes remove: node.
30-
adjacentNode incomingNodes ifEmpty: [
31-
nodesWithNoIncomingEdges add: adjacentNode ] ].
32-
node adjacentNodes: #( ) ].
65+
"Remove all the edges of node from the graph"
66+
node adjacentNodes do: [ :adjacentNode |
67+
adjacentNode incomingNodes remove: node.
68+
adjacentNode incomingNodes ifEmpty: [
69+
nodesWithNoIncomingEdges add: adjacentNode ] ].
70+
node adjacentNodes: #( ) ].
3371
3472
"If the graph still has edges"
3573
(nodes anySatisfy: [ :node | node adjacentNodes isNotEmpty ])
3674
ifTrue: [ Error signal: 'Not a DAG (Directed Acyclic Graph)' ].
3775
3876
"Return the topological order the first element being the node without any dependencies"
39-
^ topologicalSortedElements```### Improving the implementationThe method `run` is a bit too long to our taste.The fact that we have to add comments to separate its code logic is a call to define separate methods.First we define two methods `initializeElements` and `gatherNoIncomingNodes`.And we express the algorithm by redefining the method `run` as follows:```AITopologicalSorting >> initializeElements
40-
41-
topologicalSortedElements := OrderedCollection new.
42-
nodesWithNoIncomingEdges := LinkedList new```Note that `nodesWithNoIncomingEdge` uses a linked list because it hasa better time complexity for removing the first element.```AITopologicalSorting >> gatherNoIncomingNodes
43-
"Obtain all the nodes without incoming nodes"
44-
45-
nodesWithNoIncomingEdges addAll:
46-
(nodes select: [ :node | node isLeaf ]).``````AITopologicalSorting >> run
47-
48-
self initializeElements.
49-
self gatherNoIncomingNodes.
50-
[ nodesWithNoIncomingEdges isNotEmpty ] whileTrue: [
51-
| node |
52-
node := nodesWithNoIncomingEdges removeFirst.
53-
...```Then we continue by extracting the handling of node dependencies and extract the validation.```AITopologicalSorting >> removeEdgesOf: node
54-
55-
node adjacentNodes do: [ :adjacentNode |
56-
adjacentNode incomingNodes remove: node.
57-
adjacentNode incomingNodes ifEmpty: [
58-
nodesWithNoIncomingEdges add: adjacentNode ] ].
59-
node adjacentNodes: #( )].``````AITopologicalSorting >> graphHasEdges
60-
61-
^ nodes anySatisfy: [ :node | node adjacentNodes isNotEmpty ].``````AITopologicalSorting >> run
62-
63-
self initializeElements.
64-
self gatherNoIncomingNodes.
65-
[ nodesWithNoIncomingEdges isNotEmpty ] whileTrue: [
66-
| node |
67-
node := nodesWithNoIncomingEdges removeFirst.
68-
topologicalSortedElements addLast: node model.
69-
self removeEdgesOf: node ].
70-
71-
self graphHasEdges ifTrue: [
72-
Error signal: 'Not a DAG (Directed Acyclic Graph)' ].
73-
^ topologicalSortedElements```Now the logic of the algorithm is clearer and at a nice level of abstraction.In addition we can focus on the structure of the algorithm, treated nodes are added one by one to the `topologicalSortedElements` collectionwhile dependents are added to the working list `nodesWithNoIncomingEdges`.And the algorithm iterates until the working list gets empty.### Case studyImage that the graph shown in Figure *@soft@* represents software dependencies. You want to install the module _G_. But, to installthat module you must install all the other ones before in a topological order. You need to install module _C_ and _A_ before installingmodule _D_. So in this case the topological sorting is the algorithm that we need to solve the problem.![Software modules.](figures/topological_sorting.pdf width=45&label=soft)To solve this problem programatically we only need to declare the nodes, the edges and the run the algorithm.```"First define the nodes and the edges"
77+
^ topologicalSortedElements
78+
```
79+
80+
### Improving the implementation
81+
82+
The method `run` is a bit too long to our taste.
83+
The fact that we have to add comments to separate its code logic is a call to define separate methods.
84+
85+
First we define two methods `initializeElements` and `gatherNoIncomingNodes`.
86+
And we express the algorithm by redefining the method `run` as follows:
87+
88+
```
89+
AITopologicalSorting >> initializeElements
90+
91+
topologicalSortedElements := OrderedCollection new.
92+
nodesWithNoIncomingEdges := LinkedList new
93+
```
94+
95+
Note that `nodesWithNoIncomingEdge` uses a linked list because it has
96+
a better time complexity for removing the first element.
97+
98+
```
99+
AITopologicalSorting >> gatherNoIncomingNodes
100+
"Obtain all the nodes without incoming nodes"
101+
102+
nodesWithNoIncomingEdges addAll:
103+
(nodes select: [ :node | node isLeaf ]).
104+
```
105+
106+
```
107+
AITopologicalSorting >> run
108+
109+
self initializeElements.
110+
self gatherNoIncomingNodes.
111+
[ nodesWithNoIncomingEdges isNotEmpty ] whileTrue: [
112+
| node |
113+
node := nodesWithNoIncomingEdges removeFirst.
114+
...
115+
```
116+
117+
Then we continue by extracting the handling of node dependencies and extract the validation.
118+
119+
```
120+
AITopologicalSorting >> removeEdgesOf: node
121+
122+
node adjacentNodes do: [ :adjacentNode |
123+
adjacentNode incomingNodes remove: node.
124+
adjacentNode incomingNodes ifEmpty: [
125+
nodesWithNoIncomingEdges add: adjacentNode ] ].
126+
node adjacentNodes: #( )].
127+
```
128+
129+
```
130+
AITopologicalSorting >> graphHasEdges
131+
132+
^ nodes anySatisfy: [ :node | node adjacentNodes isNotEmpty ].
133+
```
134+
135+
```
136+
AITopologicalSorting >> run
137+
138+
self initializeElements.
139+
self gatherNoIncomingNodes.
140+
[ nodesWithNoIncomingEdges isNotEmpty ] whileTrue: [
141+
| node |
142+
node := nodesWithNoIncomingEdges removeFirst.
143+
topologicalSortedElements addLast: node model.
144+
self removeEdgesOf: node ].
145+
146+
self graphHasEdges ifTrue: [
147+
Error signal: 'Not a DAG (Directed Acyclic Graph)' ].
148+
^ topologicalSortedElements
149+
```
150+
151+
Now the logic of the algorithm is clearer and at a nice level of abstraction.
152+
In addition we can focus on the structure of the algorithm, treated nodes are added one by one to the `topologicalSortedElements` collection
153+
while dependents are added to the working list `nodesWithNoIncomingEdges`.
154+
And the algorithm iterates until the working list gets empty.
155+
156+
### Case study
157+
158+
Image that the graph shown in Figure *@topo@* represents software dependencies. You want to install the module _G_. But, to install
159+
that module you must install all the other ones before in a topological order. You need to install module _C_ and _A_ before installing
160+
module _D_. So in this case the topological sorting is the algorithm that we need to solve the problem.
161+
162+
![Software modules.](figures/topological_sorting.pdf width=45&label=topo)
163+
164+
To solve this problem programatically we only need to declare the nodes, the edges and the run the algorithm.
165+
166+
```
167+
"First define the nodes and the edges"
74168
nodes := #( $A $B $C $D $E $F $G ).
75169
edges := #( #( $A $B ) #( $A $C ) #( $B $E ) #( $C $E ) #( $C $D )
76170
#( $D $E ) #( $D $F ) #( $E $G ) #( $F $G ) ).
77171
"Instantiate the graph algorithm"
78172
topSortingAlgo := AITopologicalSorting new.
79173
"Set the nodes and edges"
80174
topSortingAlgo
81-
nodes: nodes;
82-
edges: edges from: #first to: #second.
175+
nodes: nodes;
176+
edges: edges from: #first to: #second.
83177
"Run to obtain the result"
84-
topologicalSortedElements := topSortingAlgo run.```Note that a _DAG_ may have several topological orders which all of them are correct. If we look at the result we get theorder in which the software dependencies need to be installed.`#( $A $B $C $D $E $F $G )`### ConclusionTopological sorting is simple algorithm working with a working list, the list where we add the next nodes to be treated.It shows that the algorithm works until the list gets empty.This is the typical structure of iterating algorithms based on working list.
178+
topologicalSortedElements := topSortingAlgo run.
179+
```
180+
181+
Note that a _DAG_ may have several topological orders which all of them are correct. If we look at the result we get the
182+
order in which the software dependencies need to be installed.
183+
184+
`#( $A $B $C $D $E $F $G )`
185+
186+
### Conclusion
187+
188+
Topological sorting is simple algorithm working with a working list, the list where we add the next nodes to be treated.
189+
It shows that the algorithm works until the list gets empty.
190+
This is the typical structure of iterating algorithms based on working list.

Chapters/Chapter5/chapter5.md

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
## Shortest Path
22

3-
The Shortest Path problem consists on finding a path between two pairs of nodes in which the sum of the weights is minimized. For a general graph, this problem is NP-hard. For some kind of graphs, like trees or directed acyclic graphs, this problem can be solved in linear time.
3+
The Shortest Path problem consists on finding a path between two pairs of nodes in which the sum of the weights is minimized. For a general graph, this problem is NP-hard. For some kind of graphs, like trees or other directed acyclic graphs, this problem can be solved in linear time.
44

55
In this chapter, we will present multiple algorithms to solve the shortest path problem, including case studies for some of them. Most of the algorithms find the solution for one single source to all other nodes. One finds the solution for a single source to a single sink, and one resolves the shortest path between all pairs of nodes. The following table is an overview of the algorithms.
66

@@ -171,7 +171,7 @@ The choice between BFS and DFS shortest path search depends on the structure of
171171

172172
### Dijkstra's algorithm
173173

174-
The Dijkstra's algorithm is one of the most-know algorithms for calculating the shortest path in a weighted graph. As BFS, this algorithm is also a single-source shortest path algorithm. In its naive implementation, it has a time complexity of $O(V^2)$. But, it can be optimized using a heap or a Fibonacci heap as a data structure to a time complexity of $O((V+E)*log V)$. If a Fibonacci heap is used we get the best possible time complexity $O(E + V * log V)$ possible \(at the moment\). Dijkstra's algorithm can handle a graph with cycles, but it cannot handle negative weights.
174+
The Dijkstra's algorithm is one of the most-know algorithms for calculating the shortest path in a **nonnegative weighted graph**. As BFS, this algorithm is also a single-source shortest path algorithm. In its naive implementation, it has a time complexity of $O(V^2)$. But, it can be optimized using a heap or a Fibonacci heap as a data structure to a time complexity of $O((V+E)*log V)$. If a Fibonacci heap is used we get the best possible time complexity $O(E + V * log V)$ possible \(at the moment\). Dijkstra's algorithm can handle a graph with cycles, but it cannot handle negative weights.
175175

176176
The algorithm idea is:
177177

@@ -588,6 +588,38 @@ pathFrom: sourceModel to: destinationModel
588588

589589
We see that the method is able to recognize disturbances derived from any negative cycle.
590590

591+
### Shortest path in an undirected graph
592+
593+
Excepting the algorithm for finding the shortest path in a DAG, the rest of the algorithms presented here are able to find the shortest path not only in directed but also in undirected graphs. As a matter of fact, any undirected graph is also a directed graph. You just have to specify any edges $\{u, v\}$ twice $u \to v$ and $v \to u$.
594+
595+
![An (unweighted) cyclic undirected graph.](figures/undirectedgraph.png width=70&label=undirectedgraph)
596+
597+
In Figure *@undirectedgraph@*, we see an undirected graph used in our tests. It is initialized as follows:
598+
599+
```
600+
AICyclicNonWeightedComplexFixture >> complexUndirectedGraph
601+
602+
"https://i.imgur.com/qK2zsYb.png"
603+
604+
| nodes edges graph|
605+
nodes := 0 to: 12.
606+
edges := #( #(0 7) #(0 11) #(0 8) #(1 9) #(1 10) #(2 3)
607+
#(2 12) #(3 2) #(3 4) #(3 7) #(4 3) #(5 6)
608+
#(6 7) #(6 5) #(7 3) #(7 0) #(7 11) #(7 6)
609+
#(8 9) #(8 10) #(8 0) #(9 1) #(9 8) #(9 12)
610+
#(10 1) #(10 8) #(11 7) #(11 0) #(12 2)
611+
#(12 9) ).
612+
613+
graph:= AIGraphNonWeightedFixtureStructure new.
614+
graph nodes: nodes.
615+
graph edges: edges.
616+
^graph
617+
```
618+
619+
Note that each undirected edge is represented by two corresponding directed edges.
620+
621+
However, there is a restriction in case of negative weights, since by doubling an undirected negative edge with two directed edges, the Bellman-Ford or the Floyd-Warshall algorithm would detect a negative cycle.
622+
591623
### Longest path problem
592624

593625
To calculate the longest path of a directed graph, we can simply multiply all the edge weights by $-1$ and then calculate the shortest path.
32.5 KB
Loading

0 commit comments

Comments
 (0)