ib002(graphs): convert pseudocode to Ada

cause why not…

Signed-off-by: Matej Focko <mfocko@redhat.com>
This commit is contained in:
Matej Focko 2023-02-04 18:06:49 +01:00
parent a4a9bcfcd2
commit 2c0d77b689
Signed by: mfocko
GPG key ID: 7C47D46246790496

View file

@ -21,81 +21,108 @@ On the other hand, we have seen iterative implementation in the exercises and I
### Recursive DFS implementation from exercises without colors ### Recursive DFS implementation from exercises without colors
``` ```ada showLineNumbers
FUNCTION VisitedDFS(u, Visited) IS function VisitedDFS(u: Vertex, visited: VertexSet) return VertexSet is
Visited <- Union(Visited, { u }) v: Vertex;
FOR v IN u.successors DO begin
IF v NOT IN Visited THEN visited.Union(To_Set(u));
Visited <- VisitedDFS(v, Visited)
FI
OD
RETURN Visited for v in u.successors loop
END if not Contains(visited, v) then
visited := visitedDFS(v, Visited);
end if;
end loop;
return visited;
end VisitedDFS;
``` ```
This implementation is correct, does the DFS traversal as it should, however it has one „smallish“ downside and that is the time complexity. The usage of set raises the time complexity, of course it is implementation dependant. However in case of either RB-tree or hash-table implementation, we get look-up in time $\mathcal{O}(n)$ for hash-table in worst-case or $\mathcal{O}(\log n)$ for the other in the worst-case. Both are not ideal compared to checking color on vertex. This implementation is correct, does the DFS traversal as it should, however it has one „smallish“ downside and that is the time complexity. The usage of set raises the time complexity, of course it is implementation dependant. However in case of either RB-tree or hash-table implementation, we get look-up in time $\mathcal{O}(n)$ for hash-table in worst-case or $\mathcal{O}(\log n)$ for the other in the worst-case. Both are not ideal compared to checking color on vertex.
### Iterative DFS from the exercises ### Iterative DFS from the exercises
``` ```ada showLineNumbers
PROCEDURE IterDFS(u) procedure IterDFS(u: Vertex) is
stack <- empty stack stack: StateVector;
Push(stack, (u, 0)) i, time: Integer;
u.color <- gray v: Vertex;
time <- 1 begin
u.d <- time stack.Append(VertexState(u, 0));
u.color := Gray;
time := 1;
u.d := time;
WHILE NOT Empty(stack) DO while not stack.Is_Empty loop
(u, k) <- Pop(stack) u := stack.Last_Element.Vertex;
i := stack.Last_Element.NextIndex;
stack.Delete_Last;
IF k < Length(u.successors) THEN if i < u.successors.Length then
// search is not finished, is pushed back to stack -- search is not finished, is pushed back to stack
Push(stack, (u, k + 1)) stack.Append(VertexState(u, k + 1));
v <- u.successors[k + 1] v := u.successors.Element(i);
IF v.color = white THEN if v.color = White then
Push(stack, (v, 0)) stack.Append(VertexState(v, 0));
v.color <- gray v.color := Gray;
time <- time + 1 time := time + 1;
v.d <- time v.d := time;
FI end if;
ELSE else
// u has no other successors, we can finish the search -- u has no other successors, we can finish the search
time <- time + 1 time := time + 1;
u.f <- time u.f := time;
u.color <- black u.color := Black;
FI end if;
OD end loop;
END
end IterDFS;
``` ```
As we can see, there is some ordering in which we search through the successors. Time complexity is OK, stack holds at most all vertices (they must be on the current path). As we can see, there is some ordering in which we search through the successors. Time complexity is OK, stack holds at most all vertices (they must be on the current path).
### My iterative with path in stack ### My iterative with path in stack
``` ```ada showLineNumbers
PROCEDURE DFS(G, start) IS procedure DFS(start: Vertex) is
path <- [ start ] path: VertexVector;
time <- 1 time: Integer;
start.d, start.color <- time, gray hasSuccessor: Bool;
successor: Vertex;
begin
path.Append(start);
time := 1;
WHILE NOT Empty(path) DO start.d := time;
hasSuccessor <- false start.color := Gray;
FOR successor IN path[-1].successors DO
IF successor.color = white THEN while not path.Is_Empty loop
hasSuccessor <- true hasSuccessor := false;
successor.d, successor.color <- ++time, gray
path <- Append(path, successor) for successor in path.Last_Element.successors loop
BREAK if successor.color = White then
FI hasSuccessor := true;
OD
IF NOT hasSuccessor THEN successor.d := time + 1;
lastVertex <- Pop(path) successor.color := Gray;
lastVertex.f, lastVertex.color <- ++time, black time := time + 1;
FI
OD path.Append(successor);
END
exit;
end if;
end loop;
if not hasSuccessor then
path.Last_Element.f := time + 1;
path.Last_Element.color := Black;
time := time + 1;
path.Delete_Last;
end if;
end loop;
end DFS;
``` ```
This approach is similar to the iterative solution from the exercises, but it does not keep the index of the next successor, therefore it always iterates through all of them, which raises the time complexity. This approach is similar to the iterative solution from the exercises, but it does not keep the index of the next successor, therefore it always iterates through all of them, which raises the time complexity.
@ -104,27 +131,44 @@ This approach is similar to the iterative solution from the exercises, but it do
On the other hand, we do not actually have to depend on the representation of the graph. In this case, we just _somehow_ obtain the iterator (which yields all of the succesors) and keep it in the stack. On the other hand, we do not actually have to depend on the representation of the graph. In this case, we just _somehow_ obtain the iterator (which yields all of the succesors) and keep it in the stack.
``` ```ada showLineNumbers
PROCEDURE DFS(G, start) IS procedure DFS(start: Vertex) is
path <- [ (start, Iterator(start.successors)) ] path: StateVector;
time <- 1 time: Integer;
start.d, start.color <- time, gray current: State;
nextVertex: Vertex;
begin
path.Append(State(start));
time := 1;
WHILE NOT Empty(path) DO start.d := time;
lastVertex, successors <- path[-1] start.color := Gray;
IF NOT MoveNext(successors) THEN while not path.Is_Empty loop
Pop(path) current := path.Last_Element;
lastVertex.f, lastVertex.color <- ++time, black
ELSE IF successors.Current.color = white THEN if not Move_Next(current.successors) then
nextVertex <- successors.Current path.Delete_Last;
nextVertex.d, nextVertex.color <- ++time, gray
path <- Append(path, (nextVertex, Iterator(nextVertex.successors))) time := time + 1;
FI current.vertex.f := time;
OD
END current.vertex.color := Black;
else if current.successors.Value.color = white then
nextVertex := current.successors.Value;
time := time + 1;
nextVertex.d := time;
nextVertex.color := Gray;
path.Append(State(nextVertex));
end if;
end loop;
end DFS;
``` ```
( The way we manipulate with the iterators is closest to the C# implementation. Apart from the `Iterator` thing :) In case you tried to implement it in C++, you would more than likely need to change the check, since you would get first successor right at the beginning ) ( The way we manipulate with the iterators is closest to the C# implementation. Apart from the `Iterator` thing :) In case you tried to implement it in C++, you would more than likely need to change the check, since you would get first successor right at the beginning )
So here we don't keep indices, but the iterators. We can also check existence of other successors easily: by the iterator moving after the last successor. So here we don't keep indices, but the iterators. We can also check existence of other successors easily: by the iterator moving after the last successor.