Построение минимального дерева Штейнера в евклидовой плоскости
Условие:
Даны три точки в евклидовой плоскости с координатами A(x1,y1), B(x2,y2) и C(x3,y3). Необходимо построить минимальное дерево, соединяющее эти точки с минимальной общей длиной рёбер.
Разрешается добавление одной дополнительной точки S(xs,ys)(точки Штейнера), которая может уменьшить суммарную длину соединяющих отрезков.
Требования:
- Найти координаты точки S, которая минимизирует сумму расстояний от неё до заданных точек.
- Визуализировать полученное дерево с помощью библиотеки SFML.
- Вывести терминальные точки (красным), точку Штейнера (синим) и соединяющие рёбра (жёлтым).
Формат входных данных:
На вход подаются три пары вещественных чисел (x1,y1), (x2,y2), (x3,y3) — координаты заданных точек.
Формат выходных данных:
- Вычислить координаты точки Штейнера S(xs,ys).
- Вывести общую длину построенного дерева.
- Открыть графическое окно с визуализацией результата.
Пример работы программы:
Порядок решения задачи о минимальном дереве Штейнера
Шаг 1: Определение входных данных
Заданы три точки A(x1,y1), B(x2,y2) и C(x3,y3) в евклидовой плоскости. Нужно найти минимальное дерево, соединяющее их, с возможностью добавления одной точки Штейнера S(xs,ys), которая минимизирует сумму расстояний.
Шаг 2: Поиск точки Штейнера
Метод градиентного спуска применяется для нахождения оптимальной точки S.
2. Минимизация суммы расстояний
- Двигаемся в направлениях вверх, вниз, влево и вправо с некоторым шагом Δ, уменьшая его, когда не находим лучшего положения.
- Останавливаемся, когда шаг становится меньше заданной точности.
Шаг 3: Визуализация графа (SFML)
После нахождения S(xs,ys), строим дерево:
Шаг 4: Вывод результатов
- Красные точки — исходные точки
- Синяя точка — точка Штейнера
- Жёлтые линии — минимальное дерево
Код программы
#include <SFML/Graphics.hpp>
#include <iostream>
#include <cmath>
#include <vector>
#include <limits>
using namespace std;
using namespace sf;
struct Point {
double x, y;
};
// Вычисление расстояния между двумя точками
double distance(Point a, Point b) {
return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}
// Сумма расстояний от точки p до всех остальных
double totalDistance(Point p, const vector<Point>& terminals) {
double sum = 0;
for (const auto& t : terminals) {
sum += distance(p, t);
}
return sum;
}
// Метод градиентного спуска для поиска точки Штейнера
Point steinerPoint(const vector<Point>& terminals) {
double step = 0.1;
double precision = 1e-6;
Point steiner = { 0, 0 };
for (const auto& t : terminals) {
steiner.x += t.x;
steiner.y += t.y;
}
steiner.x /= terminals.size();
steiner.y /= terminals.size();
double prevDist = totalDistance(steiner, terminals);
while (step > precision) {
bool improved = false;
for (double dx : {-step, step}) {
for (double dy : {-step, step}) {
Point newPoint = { steiner.x + dx, steiner.y + dy };
double newDist = totalDistance(newPoint, terminals);
if (newDist < prevDist) {
steiner = newPoint;
prevDist = newDist;
improved = true;
}
}
}
if (!improved) {
step /= 2;
}
}
return steiner;
}
// Основная функция
int main() {
// Размер окна
const int WIDTH = 600, HEIGHT = 600;
// Исходные точки
vector<Point> terminals = { {100, 500}, {500, 500}, {300, 100}};
// Найдём точку Штейнера
Point s = steinerPoint(terminals);
// Создание окна SFML
RenderWindow window(VideoMode(WIDTH, HEIGHT), "Steiner Tree Visualization");
while (window.isOpen()) {
Event event;
while (window.pollEvent(event)) {
if (event.type == Event::Closed)
window.close();
}
// Очистка экрана
window.clear(Color::Black);
// Рисуем рёбра (жёлтые линии)
for (const auto& t : terminals) {
Vertex line[] = {
Vertex(Vector2f(s.x, s.y), Color::Yellow),
Vertex(Vector2f(t.x, t.y), Color::Yellow)
};
window.draw(line, 2, Lines);
}
// Рисуем терминальные точки (красные)
for (const auto& t : terminals) {
CircleShape terminal(5);
terminal.setPosition(t.x - 5, t.y - 5);
terminal.setFillColor(Color::Red);
window.draw(terminal);
}
// Рисуем точку Штейнера (синяя)
CircleShape steiner(5);
steiner.setPosition(s.x - 5, s.y - 5);
steiner.setFillColor(Color::Blue);
window.draw(steiner);
// Отображение кадра
window.display();
}
return 0;
}Метод использует градиентный спуск для поиска точки Штейнера и SFML для визуализации. Это позволяет эффективно решать задачу и демонстрировать результат в графическом окне.