From 5bf246fce6d4a67e801b242780accce213752cca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claudio=20Cesar=20de=20S=C3=A1?= Date: Thu, 2 Jun 2022 01:11:29 -0300 Subject: [PATCH] examples: some new graphs algorithms and improving 2 others (#14556) --- examples/graphs/bellman-ford.v | 163 +++++++++++++++ examples/graphs/bfs2.v | 31 ++- examples/graphs/dfs.v | 22 +- examples/graphs/dijkstra.v | 241 ++++++++++++++++++++++ examples/graphs/minimal_spann_tree_prim.v | 230 +++++++++++++++++++++ 5 files changed, 661 insertions(+), 26 deletions(-) create mode 100644 examples/graphs/bellman-ford.v create mode 100644 examples/graphs/dijkstra.v create mode 100644 examples/graphs/minimal_spann_tree_prim.v diff --git a/examples/graphs/bellman-ford.v b/examples/graphs/bellman-ford.v new file mode 100644 index 0000000000..637d50be89 --- /dev/null +++ b/examples/graphs/bellman-ford.v @@ -0,0 +1,163 @@ +/* +A V program for Bellman-Ford's single source +shortest path algorithm. +literaly adapted from: +https://www.geeksforgeeks.org/bellman-ford-algorithm-dp-23/ +// Adapted from this site... from C++ and Python codes + +For Portugese reference +http://rascunhointeligente.blogspot.com/2010/10/o-algoritmo-de-bellman-ford-um.html + +By CCS +*/ + +const large = 999999 // almost inifinity + +// a structure to represent a weighted edge in graph +struct EDGE { +mut: + src int + dest int + weight int +} + +// building a map of with all edges etc of a graph, represented from a matrix adjacency +// Input: matrix adjacency --> Output: edges list of src, dest and weight +fn build_map_edges_from_graph(g [][]T) map[T]EDGE { + n := g.len // TOTAL OF NODES for this graph -- its dimmension + mut edges_map := map[int]EDGE{} // a graph represented by map of edges + + mut edge := 0 // a counter of edges + for i in 0 .. n { + for j in 0 .. n { + // if exist an arc ... include as new edge + if g[i][j] != 0 { + edges_map[edge] = EDGE{i, j, g[i][j]} + edge++ + } + } + } + // print('${edges_map}') + return edges_map +} + +fn print_sol(dist []int) { + n_vertex := dist.len + print('\n Vertex Distance from Source') + for i in 0 .. n_vertex { + print('\n $i --> ${dist[i]}') + } +} + +// The main function that finds shortest distances from src +// to all other vertices using Bellman-Ford algorithm. The +// function also detects negative weight cycle +fn bellman_ford(graph [][]T, src int) { + mut edges := build_map_edges_from_graph(graph) + // this function was done to adapt a graph representation + // by a adjacency matrix, to list of adjacency (using a MAP) + n_edges := edges.len // number of EDGES + + // Step 1: Initialize distances from src to all other + // vertices as INFINITE + n_vertex := graph.len // adjc matrix ... n nodes or vertex + mut dist := []int{len: n_vertex, init: large} // dist with -1 instead of INIFINITY + // mut path := []int{len: n , init:-1} // previous node of each shortest paht + dist[src] = 0 + + // Step 2: Relax all edges |V| - 1 times. A simple + // shortest path from src to any other vertex can have + // at-most |V| - 1 edges + + for _ in 0 .. n_vertex { + for j in 0 .. n_edges { + mut u := edges[j].src + mut v := edges[j].dest + mut weight := edges[j].weight + if (dist[u] != large) && (dist[u] + weight < dist[v]) { + dist[v] = dist[u] + weight + } + } + } + + // Step 3: check for negative-weight cycles. The above + // step guarantees shortest distances if graph doesn't + // contain negative weight cycle. If we get a shorter + // path, then there is a cycle. + for j in 0 .. n_vertex { + mut u := edges[j].src + mut v := edges[j].dest + mut weight := edges[j].weight + if (dist[u] != large) && (dist[u] + weight < dist[v]) { + print('\n Graph contains negative weight cycle') + // If negative cycle is detected, simply + // return or an exit(1) + return + } + } + print_sol(dist) +} + +fn main() { + // adjacency matrix = cost or weight + graph_01 := [ + [0, -1, 4, 0, 0], + [0, 0, 3, 2, 2], + [0, 0, 0, 0, 0], + [0, 1, 5, 0, 0], + [0, 0, 0, -3, 0], + ] + // data from https://www.geeksforgeeks.org/bellman-ford-algorithm-dp-23/ + + graph_02 := [ + [0, 2, 0, 6, 0], + [2, 0, 3, 8, 5], + [0, 3, 0, 0, 7], + [6, 8, 0, 0, 9], + [0, 5, 7, 9, 0], + ] + // data from https://www.geeksforgeeks.org/prims-minimum-spanning-tree-mst-greedy-algo-5/ + /* + The graph: + 2 3 + (0)--(1)--(2) + | / \ | + 6| 8/ \5 |7 + | / \ | + (3)-------(4) + 9 + */ + + /* + Let us create following weighted graph + From https://www.geeksforgeeks.org/kruskals-minimum-spanning-tree-algorithm-greedy-algo-2/?ref=lbp + 10 + 0--------1 + | \ | + 6| 5\ |15 + | \ | + 2--------3 + 4 + */ + graph_03 := [ + [0, 10, 6, 5], + [10, 0, 0, 15], + [6, 0, 0, 4], + [5, 15, 4, 0], + ] + + // To find number of coluns + // mut cols := an_array[0].len + mut graph := [][]int{} // the graph: adjacency matrix + // for index, g_value in [graph_01, graph_02, graph_03] { + for index, g_value in [graph_01, graph_02, graph_03] { + graph = g_value.clone() // graphs_sample[g].clone() // choice your SAMPLE + // allways starting by node 0 + start_node := 0 + println('\n\n Graph ${index + 1} using Bellman-Ford algorithm (source node: $start_node)') + bellman_ford(graph, start_node) + } + println('\n BYE -- OK') +} + +//================================================= diff --git a/examples/graphs/bfs2.v b/examples/graphs/bfs2.v index 8e8f92df85..93d734e05f 100644 --- a/examples/graphs/bfs2.v +++ b/examples/graphs/bfs2.v @@ -1,4 +1,4 @@ -// Author: ccs +// Author: CCS // I follow literally code in C, done many years ago fn main() { // Adjacency matrix as a map @@ -20,10 +20,9 @@ fn breadth_first_search_path(graph map[string][]string, start string, target str mut path := []string{} // ONE PATH with SUCCESS = array mut queue := []string{} // a queue ... many paths // all_nodes := graph.keys() // get a key of this map - n_nodes := graph.len // numbers of nodes of this graph // a map to store all the nodes visited to avoid cycles // start all them with False, not visited yet - mut visited := a_map_nodes_bool(n_nodes) // a map fully + mut visited := visited_init(graph) // a map fully // false ==> not visited yet: {'A': false, 'B': false, 'C': false, 'D': false, 'E': false} queue << start // first arrival for queue.len != 0 { @@ -51,19 +50,6 @@ fn breadth_first_search_path(graph map[string][]string, start string, target str return path } -// Creating a map for VISITED nodes ... -// starting by false ===> means this node was not visited yet -fn a_map_nodes_bool(size int) map[string]bool { - mut my_map := map[string]bool{} // look this map ... - base := u8(65) - mut key := base.ascii_str() - for i in 0 .. size { - key = u8(base + i).ascii_str() - my_map[key] = false - } - return my_map -} - // classical removing of a node from the start of a queue fn departure(mut queue []string) string { mut x := queue[0] @@ -71,6 +57,17 @@ fn departure(mut queue []string) string { return x } +// Creating aa map to initialize with of visited nodes .... all with false in the init +// so these nodes are NOT VISITED YET +fn visited_init(a_graph map[string][]string) map[string]bool { + mut array_of_keys := a_graph.keys() // get all keys of this map + mut temp := map[string]bool{} // attention in these initializations with maps + for i in array_of_keys { + temp[i] = false + } + return temp +} + // Based in the current node that is final, search for its parent, already visited, up to the root or start node fn build_path_reverse(graph map[string][]string, start string, final string, visited map[string]bool) []string { print('\n\n Nodes visited (true) or no (false): $visited') @@ -90,3 +87,5 @@ fn build_path_reverse(graph map[string][]string, start string, final string, vis } return path } + +//====================================================== diff --git a/examples/graphs/dfs.v b/examples/graphs/dfs.v index ba7abbb90a..f722345340 100644 --- a/examples/graphs/dfs.v +++ b/examples/graphs/dfs.v @@ -1,4 +1,4 @@ -// Author: ccs +// Author: CCS // I follow literally code in C, done many years ago fn main() { @@ -35,8 +35,7 @@ fn depth_first_search_path(graph map[string][]string, start string, target strin mut path := []string{} // ONE PATH with SUCCESS = array mut stack := []string{} // a stack ... many nodes // all_nodes := graph.keys() // get a key of this map - n_nodes := graph.len // numbers of nodes of this graph - mut visited := a_map_nodes_bool(n_nodes) // a map fully + mut visited := visited_init(graph) // a map fully with false in all vertex // false ... not visited yet: {'A': false, 'B': false, 'C': false, 'D': false, 'E': false} stack << start // first push on the stack @@ -72,14 +71,15 @@ fn depth_first_search_path(graph map[string][]string, start string, target strin return path } -// Creating a map for nodes not VISITED visited ... -// starting by false ===> means this node was not visited yet -fn a_map_nodes_bool(size int) map[string]bool { - mut my_map := map[string]bool{} // look this map ... - for i in 0 .. size { - my_map[u8(65 + i).ascii_str()] = false +// Creating aa map to initialize with of visited nodes .... all with false in the init +// so these nodes are NOT VISITED YET +fn visited_init(a_graph map[string][]string) map[string]bool { + mut array_of_keys := a_graph.keys() // get all keys of this map + mut temp := map[string]bool{} // attention in these initializations with maps + for i in array_of_keys { + temp[i] = false } - return my_map + return temp } // Based in the current node that is final, search for his parent, that is already visited, up to the root or start node @@ -101,3 +101,5 @@ fn build_path_reverse(graph map[string][]string, start string, final string, vis } return path } + +//***************************************************** diff --git a/examples/graphs/dijkstra.v b/examples/graphs/dijkstra.v new file mode 100644 index 0000000000..2c945f9ac5 --- /dev/null +++ b/examples/graphs/dijkstra.v @@ -0,0 +1,241 @@ +/* +Exploring Dijkstra, +The data example is from +https://www.geeksforgeeks.org/dijkstras-shortest-path-algorithm-greedy-algo-7/ + +by CCS +Dijkstra's single source shortest path algorithm. +The program uses an adjacency matrix representation of a graph + +This Dijkstra algorithm uses a priority queue to save +the shortest paths. The queue structure has a data +which is the number of the node, +and the priority field which is the shortest distance. + +PS: all the pre-requisites of Dijkstra are considered + +$ v run file_name.v +// Creating a executable +$ v run file_name.v -o an_executable.EXE +$ ./an_executable.EXE + +Code based from : Data Structures and Algorithms Made Easy: Data Structures and Algorithmic Puzzles, Fifth Edition (English Edition) +pseudo code written in C +This idea is quite different: it uses a priority queue to store the current +shortest path evaluted +The priority queue structure built using a list to simulate +the queue. A heap is not used in this case. +*/ + +// a structure +struct NODE { +mut: + data int // NUMBER OF NODE + priority int // Lower values priority indicate ==> higher priority +} + +// Function to push according to priority ... the lower priority is goes ahead +// The "push" always sorted in pq +fn push_pq(mut prior_queue []T, data int, priority int) { + mut temp := []T{} + lenght_pq := prior_queue.len + + mut i := 0 + for (i < lenght_pq) && (priority > prior_queue[i].priority) { + temp << prior_queue[i] + i++ + } + // INSERTING SORTED in the queue + temp << NODE{data, priority} // do the copy in the right place + // copy the another part (tail) of original prior_queue + for i < lenght_pq { + temp << prior_queue[i] + i++ + } + prior_queue = temp.clone() // I am not sure if it the right way + // IS IT THE RIGHT WAY? +} + +// Change the priority of a value/node ... exist a value, change its priority +fn updating_priority(mut prior_queue []T, search_data int, new_priority int) { + mut i := 0 + mut lenght_pq := prior_queue.len + + for i < lenght_pq { + if search_data == prior_queue[i].data { + prior_queue[i] = NODE{search_data, new_priority} // do the copy in the right place + break + } + i++ + // all the list was examined + if i >= lenght_pq { + print('\n This data $search_data does exist ... PRIORITY QUEUE problem\n') + exit(1) // panic(s string) + } + } // end for +} + +// a single departure or remove from queue +fn departure_priority(mut prior_queue []T) int { + mut x := prior_queue[0].data + prior_queue.delete(0) // or .delete_many(0, 1 ) + return x +} + +// give a NODE v, return a list with all adjacents +// Take care, only positive EDGES +fn all_adjacents(g [][]T, v int) []int { + mut temp := []int{} // + for i in 0 .. (g.len) { + if g[v][i] > 0 { + temp << i + } + } + return temp +} + +// print the costs from origin up to all nodes +fn print_solution(dist []T) { + print('Vertex \tDistance from Source') + for node in 0 .. (dist.len) { + print('\n $node ==> \t ${dist[node]}') + } +} + +// print all paths and their cost or weight +fn print_paths_dist(path []T, dist []T) { + print('\n Read the nodes from right to left (a path): \n') + + for node in 1 .. (path.len) { + print('\n $node ') + mut i := node + for path[i] != -1 { + print(' <= ${path[i]} ') + i = path[i] + } + print('\t PATH COST: ${dist[node]}') + } +} + +// check structure from: https://www.geeksforgeeks.org/dijkstras-shortest-path-algorithm-greedy-algo-7/ +// s: source for all nodes +// Two results are obtained ... cost and paths +fn dijkstra(g [][]int, s int) { + mut pq_queue := []NODE{} // creating a priority queue + push_pq(mut pq_queue, s, 0) // goes s with priority 0 + mut n := g.len + + mut dist := []int{len: n, init: -1} // dist with -1 instead of INIFINITY + mut path := []int{len: n, init: -1} // previous node of each shortest paht + + // Distance of source vertex from itself is always 0 + dist[s] = 0 + + for pq_queue.len != 0 { + mut v := departure_priority(mut pq_queue) + // for all W adjcents vertices of v + mut adjs_of_v := all_adjacents(g, v) // all_ADJ of v .... + // print('\n ADJ ${v} is ${adjs_of_v}') + mut new_dist := 0 + for w in adjs_of_v { + new_dist = dist[v] + g[v][w] + if dist[w] == -1 { + dist[w] = new_dist + push_pq(mut pq_queue, w, dist[w]) + path[w] = v // collecting the previous node -- lowest weight + } + if dist[w] > new_dist { + dist[w] = new_dist + updating_priority(mut pq_queue, w, dist[w]) + path[w] = v // + } + } + } + + // print the constructed distance array + print_solution(dist) + // print('\n \n Previous node of shortest path: ${path}') + print_paths_dist(path, dist) +} + +/* +Solution Expected +Vertex Distance from Source +0 0 +1 4 +2 12 +3 19 +4 21 +5 11 +6 9 +7 8 +8 14 +*/ + +fn main() { + // adjacency matrix = cost or weight + graph_01 := [ + [0, 4, 0, 0, 0, 0, 0, 8, 0], + [4, 0, 8, 0, 0, 0, 0, 11, 0], + [0, 8, 0, 7, 0, 4, 0, 0, 2], + [0, 0, 7, 0, 9, 14, 0, 0, 0], + [0, 0, 0, 9, 0, 10, 0, 0, 0], + [0, 0, 4, 14, 10, 0, 2, 0, 0], + [0, 0, 0, 0, 0, 2, 0, 1, 6], + [8, 11, 0, 0, 0, 0, 1, 0, 7], + [0, 0, 2, 0, 0, 0, 6, 7, 0], + ] + + graph_02 := [ + [0, 2, 0, 6, 0], + [2, 0, 3, 8, 5], + [0, 3, 0, 0, 7], + [6, 8, 0, 0, 9], + [0, 5, 7, 9, 0], + ] + // data from https://www.geeksforgeeks.org/prims-minimum-spanning-tree-mst-greedy-algo-5/ + /* + The graph: + 2 3 + (0)--(1)--(2) + | / \ | + 6| 8/ \5 |7 + | / \ | + (3)-------(4) + 9 + */ + + /* + Let us create following weighted graph + From https://www.geeksforgeeks.org/kruskals-minimum-spanning-tree-algorithm-greedy-algo-2/?ref=lbp + 10 + 0--------1 + | \ | + 6| 5\ |15 + | \ | + 2--------3 + 4 + */ + graph_03 := [ + [0, 10, 6, 5], + [10, 0, 0, 15], + [6, 0, 0, 4], + [5, 15, 4, 0], + ] + + // To find number of coluns + // mut cols := an_array[0].len + mut graph := [][]int{} // the graph: adjacency matrix + // for index, g_value in [graph_01, graph_02, graph_03] { + for index, g_value in [graph_01, graph_02, graph_03] { + graph = g_value.clone() // graphs_sample[g].clone() // choice your SAMPLE + // allways starting by node 0 + start_node := 0 + println('\n\n Graph ${index + 1} using Dijkstra algorithm (source node: $start_node)') + dijkstra(graph, start_node) + } + + println('\n BYE -- OK') +} + +//******************************************************************** diff --git a/examples/graphs/minimal_spann_tree_prim.v b/examples/graphs/minimal_spann_tree_prim.v new file mode 100644 index 0000000000..280b599f88 --- /dev/null +++ b/examples/graphs/minimal_spann_tree_prim.v @@ -0,0 +1,230 @@ +/* +Exploring PRIMS, +The data example is from +https://www.geeksforgeeks.org/prims-minimum-spanning-tree-mst-greedy-algo-5/ + +by CCS + +PS: all the pre-requisites of Dijkstra are considered + +$ v run file_name.v +Creating a executable +$ v run file_name.v -o an_executable.EXE +$ ./an_executable.EXE + +Code based from : Data Structures and Algorithms Made Easy: Data Structures and Algorithmic Puzzles, Fifth Edition (English Edition) +pseudo code written in C +This idea is quite different: it uses a priority queue to store the current +shortest path evaluted +The priority queue structure built using a list to simulate +the queue. A heap is not used in this case. +*/ + +// a structure +struct NODE { +mut: + data int // number of nodes + priority int // Lower values priority indicate ==> higher priority +} + +// Function to push according to priority ... the lower priority is goes ahead +// The "push" always sorted in pq +fn push_pq(mut prior_queue []T, data int, priority int) { + mut temp := []T{} + lenght_pq := prior_queue.len + + mut i := 0 + for (i < lenght_pq) && (priority > prior_queue[i].priority) { + temp << prior_queue[i] + i++ + } + // INSERTING SORTED in the queue + temp << NODE{data, priority} // do the copy in the right place + // copy the another part (tail) of original prior_queue + for i < lenght_pq { + temp << prior_queue[i] + i++ + } + prior_queue = temp.clone() + // I am not sure if it the right way + // IS IT THE RIGHT WAY? +} + +// Change the priority of a value/node ... exist a value, change its priority +fn updating_priority(mut prior_queue []T, search_data int, new_priority int) { + mut i := 0 + mut lenght_pq := prior_queue.len + + for i < lenght_pq { + if search_data == prior_queue[i].data { + prior_queue[i] = NODE{search_data, new_priority} // do the copy in the right place + break + } + i++ + // all the list was examined + if i >= lenght_pq { + // print('\n Priority Queue: ${prior_queue}') + // print('\n These data ${search_data} and ${new_priority} do not exist ... PRIORITY QUEUE problem\n') + // if it does not find ... then push it + push_pq(mut prior_queue, search_data, new_priority) + // exit(1) // panic(s string) + } + } // end for +} + +// a single departure or remove from queue +fn departure_priority(mut prior_queue []T) int { + mut x := prior_queue[0].data + prior_queue.delete(0) // or .delete_many(0, 1 ) + return x +} + +// give a NODE v, return a list with all adjacents +// Take care, only positive EDGES +fn all_adjacents(g [][]T, v int) []int { + mut temp := []int{} // + for i in 0 .. (g.len) { + if g[v][i] > 0 { + temp << i + } + } + return temp +} + +// print the costs from origin up to all nodes +// A utility function to print the +// constructed MST stored in parent[] +// print all paths and their cost or weight +fn print_solution(path []int, g [][]int) { + // print(' PATH: ${path} ==> ${path.len}') + print(' Edge \tWeight\n') + mut sum := 0 + for node in 0 .. (path.len) { + if path[node] == -1 { + print('\n $node <== reference or start node') + } else { + print('\n $node <--> ${path[node]} \t${g[node][path[node]]}') + sum += g[node][path[node]] + } + } + print('\n Minimum Cost Spanning Tree: $sum\n\n') +} + +// check structure from: https://www.geeksforgeeks.org/dijkstras-shortest-path-algorithm-greedy-algo-7/ +// s: source for all nodes +// Two results are obtained ... cost and paths +fn prim_mst(g [][]int, s int) { + mut pq_queue := []NODE{} // creating a priority queue + push_pq(mut pq_queue, s, 0) // goes s with priority 0 + mut n := g.len + + mut dist := []int{len: n, init: -1} // dist with -1 instead of INIFINITY + mut path := []int{len: n, init: -1} // previous node of each shortest paht + + // Distance of source vertex from itself is always 0 + dist[s] = 0 + + for pq_queue.len != 0 { + mut v := departure_priority(mut pq_queue) + // for all W adjcents vertices of v + mut adjs_of_v := all_adjacents(g, v) // all_ADJ of v .... + // print('\n :${dist} :: ${pq_queue}') + // print('\n ADJ ${v} is ${adjs_of_v}') + mut new_dist := 0 + for w in adjs_of_v { + new_dist = dist[v] + g[v][w] + + if dist[w] == -1 { + dist[w] = g[v][w] + push_pq(mut pq_queue, w, dist[w]) + path[w] = v // collecting the previous node -- lowest weight + } + + if dist[w] > new_dist { + dist[w] = g[v][w] // new_dist// + updating_priority(mut pq_queue, w, dist[w]) + path[w] = v // father / previous node + } + } + } + + // print('\n \n Previous node of shortest path: ${path}') + // print_paths_dist(path , dist) + print_solution(path, g) +} + +/* +Solution Expected graph_02 +Edge Weight +0 - 1 2 +1 - 2 3 +0 - 3 6 +1 - 4 5 +*/ + +fn main() { + // adjacency matrix = cost or weight + graph_01 := [ + [0, 4, 0, 0, 0, 0, 0, 8, 0], + [4, 0, 8, 0, 0, 0, 0, 11, 0], + [0, 8, 0, 7, 0, 4, 0, 0, 2], + [0, 0, 7, 0, 9, 14, 0, 0, 0], + [0, 0, 0, 9, 0, 10, 0, 0, 0], + [0, 0, 4, 14, 10, 0, 2, 0, 0], + [0, 0, 0, 0, 0, 2, 0, 1, 6], + [8, 11, 0, 0, 0, 0, 1, 0, 7], + [0, 0, 2, 0, 0, 0, 6, 7, 0], + ] + + graph_02 := [ + [0, 2, 0, 6, 0], + [2, 0, 3, 8, 5], + [0, 3, 0, 0, 7], + [6, 8, 0, 0, 9], + [0, 5, 7, 9, 0], + ] + // data from https://www.geeksforgeeks.org/prims-minimum-spanning-tree-mst-greedy-algo-5/ + /* + The graph: + 2 3 + (0)--(1)--(2) + | / \ | + 6| 8/ \5 |7 + | / \ | + (3)-------(4) + 9 + */ + + /* + Let us create following weighted graph + From https://www.geeksforgeeks.org/kruskals-minimum-spanning-tree-algorithm-greedy-algo-2/?ref=lbp + 10 + 0--------1 + | \ | + 6| 5\ |15 + | \ | + 2--------3 + 4 + */ + graph_03 := [ + [0, 10, 6, 5], + [10, 0, 0, 15], + [6, 0, 0, 4], + [5, 15, 4, 0], + ] + + // To find number of coluns + // mut cols := an_array[0].len + mut graph := [][]int{} // the graph: adjacency matrix + // for index, g_value in [graph_01, graph_02, graph_03] { + for index, g_value in [graph_01, graph_02, graph_03] { + println('\n Minimal Spanning Tree of graph ${index + 1} using PRIM algorithm') + graph = g_value.clone() // graphs_sample[g].clone() // choice your SAMPLE + // starting by node x ... see the graphs dimmension + start_node := 0 + prim_mst(graph, start_node) + } + println('\n BYE -- OK') +} + +//********************************************************************