Построение минимального дерева Штейнера в евклидовой плоскости
Условие:
Даны три точки в евклидовой плоскости с координатами 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 для визуализации. Это позволяет эффективно решать задачу и демонстрировать результат в графическом окне.