Boost Kruskal 最小生成树算法——无向图与有向图文档
Boost Kruskal minimum spanning tree algorithm -- undirected vs directed graph documentation
根据 documentation,boost 中实现的最小生成树算法应该只适用于无向图。然而,以下提供有向图作为算法输入的代码似乎工作正常:(在 MSVC Visual Studio 2019 上构建时,没有与 boost 相关的警告)
#include <boost/property_map/property_map.hpp>
#include <boost/config.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/kruskal_min_spanning_tree.hpp>
#include <boost/graph/graph_utility.hpp>
using namespace boost;
typedef adjacency_list <vecS, vecS, directedS, no_property,
property<edge_weight_t, double>>
Graph_vvd_MST;
typedef adjacency_list_traits<vecS, vecS, directedS> Traits_vvd;
property_map<Graph_vvd_MST, edge_weight_t>::type cost;
typedef Traits_vvd::edge_descriptor Edge;
std::vector < Edge > spanning_tree;
int main() {
Graph_vvd_MST g;
add_vertex(g);//0 th index vertex
add_vertex(g);// 1 index vertex
add_vertex(g);// 2 index vertex
cost = get(edge_weight, g);
//Add directed arcs
for(int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++) {
if (i == j)
continue;
std::pair<Traits_vvd::edge_descriptor, bool> AE = add_edge(i, j, g);
assert(AE.second);
if (i == 0 && j == 1) cost[AE.first] = 1;
if (i == 0 && j == 2) cost[AE.first] = 2;
if (i == 1 && j == 0) cost[AE.first] = 1;
if (i == 1 && j == 2) cost[AE.first] = 2;
if (i == 2 && j == 0) cost[AE.first] = 1;
if (i == 2 && j == 1) cost[AE.first] = 2;
}
kruskal_minimum_spanning_tree(g, std::back_inserter(spanning_tree));
printf("MST Solution:\n");
for (std::vector < Edge >::iterator ei = spanning_tree.begin();
ei != spanning_tree.end(); ++ei) {
int fr = source(*ei, g);
int to = target(*ei, g);
double cst = cost[*ei];
printf("[%d %d]: %f \n", fr, to, cst);
}
getchar();
}
上面的代码生成以下双向图:
代码正确输出:
MST Solution:
[0 1]: 1.000000
[2 0]: 1.000000
是不是文档没有更新,在最近的boost版本中,该算法实际上可以使用有向图?
我会简化代码 Live
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/kruskal_min_spanning_tree.hpp>
#include <iostream>
using Graph =
boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS,
boost::no_property,
boost::property<boost::edge_weight_t, double>>;
using Edge = Graph::edge_descriptor;
int main()
{
Graph g(3); // 0..2
/*auto [it, ok] =*/ add_edge(0, 1, {1}, g);
add_edge(0, 2, {2}, g);
add_edge(1, 0, {1}, g);
add_edge(1, 2, {2}, g);
add_edge(2, 0, {1}, g);
add_edge(2, 1, {2}, g);
auto cost = get(boost::edge_weight, g);
std::vector<Edge> spanning_tree;
kruskal_minimum_spanning_tree(g, std::back_inserter(spanning_tree));
std::cout << "MST Solution:\n";
for (auto e : spanning_tree) {
std::cout << e << ": " << cost[e] << "\n";
}
}
如果坚持循环插入边:Live
for (auto [i, j, c] : { std::tuple //
{0, 1, 1.},
{0, 2, 2.},
{1, 0, 1.},
{1, 2, 2.},
{2, 0, 1.},
{2, 1, 2.},
})
{
if (!add_edge(i, j, {c}, g).second)
return 255;
}
问题
如果您不满足记录的 pre-conditions 输出是未指定的。未指定并不意味着它会抛出异常(即 specified)。它甚至可能不小心(似乎)做了正确的事情。
关键是该算法的运行假设边缘 - 根据定义 - 可以以相同的成本反向遍历。一旦偏离,算法可能会给出不正确的结果。事实上,某些算法可能会表现出 未定义的行为(例如,带有一些负权重的 Dijkstra 可能永远不会完成)。
你最好
- 要么将你的图转换为无向图
- 满足无向图的不变量并验证算法是否正确工作
- 使用 MDST(最小有向生成树)算法,参见例如Finding a minimum spanning tree on a directed graph
根据 documentation,boost 中实现的最小生成树算法应该只适用于无向图。然而,以下提供有向图作为算法输入的代码似乎工作正常:(在 MSVC Visual Studio 2019 上构建时,没有与 boost 相关的警告)
#include <boost/property_map/property_map.hpp>
#include <boost/config.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/kruskal_min_spanning_tree.hpp>
#include <boost/graph/graph_utility.hpp>
using namespace boost;
typedef adjacency_list <vecS, vecS, directedS, no_property,
property<edge_weight_t, double>>
Graph_vvd_MST;
typedef adjacency_list_traits<vecS, vecS, directedS> Traits_vvd;
property_map<Graph_vvd_MST, edge_weight_t>::type cost;
typedef Traits_vvd::edge_descriptor Edge;
std::vector < Edge > spanning_tree;
int main() {
Graph_vvd_MST g;
add_vertex(g);//0 th index vertex
add_vertex(g);// 1 index vertex
add_vertex(g);// 2 index vertex
cost = get(edge_weight, g);
//Add directed arcs
for(int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++) {
if (i == j)
continue;
std::pair<Traits_vvd::edge_descriptor, bool> AE = add_edge(i, j, g);
assert(AE.second);
if (i == 0 && j == 1) cost[AE.first] = 1;
if (i == 0 && j == 2) cost[AE.first] = 2;
if (i == 1 && j == 0) cost[AE.first] = 1;
if (i == 1 && j == 2) cost[AE.first] = 2;
if (i == 2 && j == 0) cost[AE.first] = 1;
if (i == 2 && j == 1) cost[AE.first] = 2;
}
kruskal_minimum_spanning_tree(g, std::back_inserter(spanning_tree));
printf("MST Solution:\n");
for (std::vector < Edge >::iterator ei = spanning_tree.begin();
ei != spanning_tree.end(); ++ei) {
int fr = source(*ei, g);
int to = target(*ei, g);
double cst = cost[*ei];
printf("[%d %d]: %f \n", fr, to, cst);
}
getchar();
}
上面的代码生成以下双向图:
代码正确输出:
MST Solution:
[0 1]: 1.000000
[2 0]: 1.000000
是不是文档没有更新,在最近的boost版本中,该算法实际上可以使用有向图?
我会简化代码 Live
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/kruskal_min_spanning_tree.hpp>
#include <iostream>
using Graph =
boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS,
boost::no_property,
boost::property<boost::edge_weight_t, double>>;
using Edge = Graph::edge_descriptor;
int main()
{
Graph g(3); // 0..2
/*auto [it, ok] =*/ add_edge(0, 1, {1}, g);
add_edge(0, 2, {2}, g);
add_edge(1, 0, {1}, g);
add_edge(1, 2, {2}, g);
add_edge(2, 0, {1}, g);
add_edge(2, 1, {2}, g);
auto cost = get(boost::edge_weight, g);
std::vector<Edge> spanning_tree;
kruskal_minimum_spanning_tree(g, std::back_inserter(spanning_tree));
std::cout << "MST Solution:\n";
for (auto e : spanning_tree) {
std::cout << e << ": " << cost[e] << "\n";
}
}
如果坚持循环插入边:Live
for (auto [i, j, c] : { std::tuple //
{0, 1, 1.},
{0, 2, 2.},
{1, 0, 1.},
{1, 2, 2.},
{2, 0, 1.},
{2, 1, 2.},
})
{
if (!add_edge(i, j, {c}, g).second)
return 255;
}
问题
如果您不满足记录的 pre-conditions 输出是未指定的。未指定并不意味着它会抛出异常(即 specified)。它甚至可能不小心(似乎)做了正确的事情。
关键是该算法的运行假设边缘 - 根据定义 - 可以以相同的成本反向遍历。一旦偏离,算法可能会给出不正确的结果。事实上,某些算法可能会表现出 未定义的行为(例如,带有一些负权重的 Dijkstra 可能永远不会完成)。
你最好
- 要么将你的图转换为无向图
- 满足无向图的不变量并验证算法是否正确工作
- 使用 MDST(最小有向生成树)算法,参见例如Finding a minimum spanning tree on a directed graph