Я изучаю алгоритм Фрухтермана-Рейнгольда в Библиотеке графов ускорения. Читая документ, я знаю, что алгоритм состоит в том, чтобы вычислять позиции для всех узлов с точки зрения разметки графа, но моя проблема в том, что я не могу понять шаги вычисления сил притяжения в Boost Graph Library.
Например, если топология является прямоугольником с высотой 100 и шириной 100, каждая вершина помечается как строка, а отношение между каждой парной вершиной как:
"0" "5""Kevin" "Martin""Ryan" "Leo""Y" "S""Kevin" "S""American" "USA"
Каждая строка обозначает две помеченные вершины связаны. Формула силы притяжения для каждой вершины должна быть:
f = (d^2) / k
где d
это расстояние между двумя вершинами и k
это оптимальные расстояния. Но я не понимаю, как пройти расстояние d
в коде Фрухтермана-Рейнгольда в библиотеке графов ускорения. В этом примере он вычисляет разницу значений ASCII между вершинами каждой пары как расстояние d
? (Значение ASCII, равное 0, равно 48, а значение ASCII, равное 5, равно 53. Верно ли, что Fruchterman-Reingold вычисляет 53 — 48 = 5 как d в BGL?) Я действительно ценю, если кто-нибудь может мне помочь.
Реализация Furchterman-Reingold использует топологию IN / OUT.
Ожидается, что топология будет инициализирована до некоторого состояния перед выполнением. Расстояние, пройденное до функции притяжения, будет тем же, что и в топологии на этой итерации.
Заметка Обратите внимание, что (если
progressive
установлен вtrue
Furterman-Reingold по умолчанию инициализирует топологию случайным образом (используяrandom_graph_layout
).
Все вышеперечисленное взято из в документации.
Вот небольшая демонстрация, использующая ваш входной граф, который показывает, как реализовать такую функцию average_force:
struct AttractionF {
template <typename EdgeDescriptor, typename Graph>
double operator()(EdgeDescriptor /*ed*/, double k, double d, Graph const& /*g*/) const {
//std::cout << "DEBUG af('" << g[source(ed, g)].name << " -> " << g[target(ed, g)].name << "; k:" << k << "; d:" << d << ")\n";
return (d*d/k);
}
};
Увидеть Жить на Колиру
#include <memory>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/fruchterman_reingold.hpp>
#include <boost/graph/random_layout.hpp>
#include <libs/graph/src/read_graphviz_new.cpp>
#include <boost/graph/topology.hpp>
#include <boost/random.hpp>
using namespace boost;
struct Vertex {
std::string name;
};
struct AttractionF {
template <typename EdgeDescriptor, typename Graph>
double operator()(EdgeDescriptor /*ed*/, double k, double d, Graph const& /*g*/) const {
//std::cout << "DEBUG af('" << g[source(ed, g)].name << " -> " << g[target(ed, g)].name << "; k:" << k << "; d:" << d << ")\n";
return (d*d/k);
}
};
using Graph = adjacency_list<vecS, vecS, undirectedS, Vertex>;
Graph make_sample();
int main() {
auto g = make_sample();
using Topology = square_topology<boost::mt19937>;
using Position = Topology::point_type;
std::vector<Position> positions(num_vertices(g));
square_topology<boost::mt19937> topology;
random_graph_layout(g,
make_iterator_property_map(positions.begin(), boost::identity_property_map{}),
topology);
fruchterman_reingold_force_directed_layout(
g,
make_iterator_property_map(positions.begin(), boost::identity_property_map{}),
topology,
attractive_force(AttractionF())
);
dynamic_properties dp;
dp.property("node_id", get(&Vertex::name, g));
write_graphviz_dp(std::cout, g, dp);
}
Graph make_sample() {
std::string const sample_dot = R"(
graph {
"0" -- "5";
"Kevin" -- "Martin";
"Ryan" -- "Leo";
"Y" -- "S";
"Kevin" -- "S";
"American" -- "USA";
}
)";
Graph g;
dynamic_properties dp;
dp.property("node_id", get(&Vertex::name, g));
read_graphviz(sample_dot, g, dp);
return g;
}
Обратите внимание, что в C ++ 11 вы также можете использовать лямбду:
fruchterman_reingold_force_directed_layout(
g,
make_iterator_property_map(positions.begin(), boost::identity_property_map{}),
topology,
attractive_force([](Graph::edge_descriptor, double k, double d, Graph const&) { return (d*d)/k; })
);