我已经在C ++中顺序实现了Boruvka的算法,我知道该算法的一个优点是它可以很容易地并行。我试图使用openMP这样做,但我无法弄清楚如何让它工作。我从graph.txt中读入邻接列表,并将最小生成树的输出打印到mst.txt中。这是我对boruvka的顺序代码:
#include <iostream>
#include <fstream>
#include <sstream>
using namespace std;
// initialize data structure for edges (given in adjacency list)
struct Edge {
int v1, v2, weight; // 2 connecting verticies and a weight
};
// initialize structure for the graph
struct Graph {
int vertex, edge;
Edge* e; // undirected graph so edge from v1 to v2 is same as v2 to v1
};
// Creates a graph for #verticies and #edges using arrays
struct Graph* formGraph(int vertex, int edge)
{
Graph* graph = new Graph;
graph->vertex = vertex;
graph->edge = edge;
graph->e = new Edge[edge]; // again, v1-v2 = v2-v1
return graph;
}
// initialize structure for subsets within the graph
struct Subset {
int parent, rank; // rank will act as counter
};
// will help to find lightest edge of sets recursively
int find(struct Subset subset[], int i)
{
if (subset[i].parent != i) {
subset[i].parent = find(subset, subset[i].parent);
}
// once it is =1
return subset[i].parent;
}
// A function that does union of two sets
void Union(struct Subset subs[], int set1, int set2)
{
int root1 = find(subs, set1);
int root2 = find(subs, set2);
//union by ranking
if (subs[root1].rank < subs[root2].rank) { // if rank2 is higher thats parent
subs[root1].parent = root2;
}
else if (subs[root1].rank > subs[root2].rank) { // if rank1 is higher thats parent
subs[root2].parent = root1;
}
else // ranks are the equal so increment rank by 1
{
subs[root2].parent = root1;
subs[root1].rank++;
}
}
// the boruvka algorithm implementation
void boruvka(struct Graph* graph) {
// set data of initial graph
int vertex = graph->vertex;
int edge = graph->edge;
Edge* e = graph->e;
//initially there will always be as many subsets as there are vertices
struct Subset *subs = new Subset[vertex];
int *lightest = new int[vertex]; // array storing least weight edge
// subset for each vertex
for (int v = 0; v < vertex; v++)
{
subs[v].parent = v; // initial parent (none)
subs[v].rank = 0; // initial rank (no parent so always 0)
lightest[v] = -1; // start from -1
}
int components = vertex; // iniitial trees = number of verticies
int minWeight = 0;
// must keep going until there is only one tree
while (components > 1)
{
// lightest weight for all edges
for (int i=0; i<edge; i++)
{
// gets subsets for edges that could connect
int set1 = find(subs, e[i].v1);
int set2 = find(subs, e[i].v2);
// waste of time if they're already in same set so don't check
if (set1 == set2)
continue;
// if different then check which one is lightest
else
{
if (lightest[set1] == -1 || e[lightest[set1]].weight > e[i].weight) {
lightest[set1] = i;
}
if (lightest[set2] == -1 || e[lightest[set2]].weight > e[i].weight) {
lightest[set2] = i;
}
}
}
// making sure the wieghts are added
for (int i=0; i<vertex; i++)
{
// make sure all lightest edges are included
if (lightest[i] != -1)
{
int s1 = find(subs, e[lightest[i]].v1);
int s2 = find(subs, e[lightest[i]].v2);
if (s1 == s2)
continue;
minWeight += e[lightest[i]].weight;
// Need to sort output lexicographically!?!?!?!?!!
printf("Edge %d-%d included in MST with weight %d\n", // prints verices and weight of edge
e[lightest[i]].v1, e[lightest[i]].v2,
e[lightest[i]].weight);
// union subsets together, decrease component number
Union(subs, s1, s2);
components--;
}
lightest[i] = -1; // in case after first iteration lightest edges fall in same subset
}
}
printf("Weight of MST is %d\n", minWeight);
return;
}
// main function for calling boruvka
int main() {
ifstream infile;
char inputFileName[] = "graph.txt"; // input filename here
infile.open(inputFileName, ios::in);
string line;
getline(infile, line);
int V = atoi(line.c_str()); // set num of vertices to first line of txt
getline(infile, line);
int E = atoi(line.c_str()); // set num of edges to second line of txt
// create graph for boruvka
struct Graph* graph = formGraph(V, E);
if (infile.is_open()) {
string data[3]; // initialize data array
int count = 0; // initialize counter
while (infile.good()) { // same as while not end of file
getline(infile, line);
stringstream ssin(line);
int i = 0;
while (ssin.good() && i < 3) {
ssin >> data[i];
i++;
}
graph->e[count].v1 = atoi(data[0].c_str());
graph->e[count].v2 = atoi(data[1].c_str());
graph->e[count].weight = atoi(data[2].c_str());
count++;
}
}
freopen("mst.txt","w",stdout); // writes output into mst.txt
// call boruvka function
boruvka(graph);
infile.close(); // close the input file
return 0;
}
我的graph.txt的一个例子就是:
9
14
0 1 4
7 8 7
1 2 8
1 7 11
2 3 7
2 5 4
2 8 2
3 4 9
3 5 14
4 5 10
5 6 2
6 7 1
6 8 6
0 7 8
这个示例的输出是正确的,放在我的mst.txt中是:
Edge 0-1 included in MST with weight 4
Edge 2-8 included in MST with weight 2
Edge 2-3 included in MST with weight 7
Edge 3-4 included in MST with weight 9
Edge 5-6 included in MST with weight 2
Edge 6-7 included in MST with weight 1
Edge 1-2 included in MST with weight 8
Edge 2-5 included in MST with weight 4
Weight of MST is 37
答案 0 :(得分:0)
根据该算法,在每次迭代中,森林中的每棵树将独立地添加到森林中的一个且仅一个边缘(来自不同树的边缘可以是相同的),直到添加的边缘将整个森林连接到单树。
在这里你可以看到找到每棵树的唯一边缘可以并行完成。只要您有多个树,就可以使用多个线程来加速搜索。
答案 1 :(得分:0)
如果您有兴趣,我已经使用OpenMP编写了implementation并行Boruvka算法。
我们将图形存储为边列表(edges
),其中每个边(u, v)
出现两次:作为u
和v
的边。在算法的每个步骤中,边沿均以O(E log E) = O(E log V)
的时间排序。
然后在P
个处理器之间分割边缘。它们每个都从其本地节点计算最短边的数组。因为为所有节点分配原始内存是在恒定时间内完成的,所以我们可以简单地将其存储为数组,而避免使用哈希映射。然后,我们使用比较和交换将处理器之间的结果合并为全局最短边数组。请注意,因为我们之前对边缘列表进行了排序,所以u
中的所有边缘在边缘中构成了一个连续的段。因此,cas循环中的额外迭代总数不会超过O(P)
,这使我们有O(E / P + P) = O(E / P)
的时间来进行此步骤。
此后,我们可以使用并行DSU算法在O(V * alpha(V) / P)
时间内沿添加的边合并组件。
下一步是更新顶点和边的列表,可以分别使用O(V / P)
和O(E / P)
中的并行累加和来完成。
由于迭代的总数为O(log V)
,所以总的时间复杂度为O(E log^2 V / P)
。