2014 dxdy logo

Научный форум dxdy

Математика, Физика, Computer Science, Machine Learning, LaTeX, Механика и Техника, Химия,
Биология и Медицина, Экономика и Финансовая Математика, Гуманитарные науки


Правила форума


Посмотреть правила форума



Начать новую тему Ответить на тему
 
 Расчет веса графа
Сообщение07.01.2025, 14:22 
Аватара пользователя


12/10/16
643
Almaty, Kazakhstan
Условия:
1) Ориентированное ребро графа весит 1 и обозначается стрелкой -> и показывает направление A -> B, из вершины A в B но не обратно.
2) Неориентированное ребро весит 2 и обозначается "ромбом" -<>- пример A -<>- B в обе стороны.
3) Граф должен быть связным, из любой вершины можно пройти в любую другую вершину.
4) Радиус графа не должен превышать 3 (максимальное расстояние между вершинами должно быть 3),
то есть из одной вершины в другой разрешено строить не больше 3 ребер, ориентированное или нет по усмотрению.

Задача построить граф, из $n$ вершин, с наименьшим количеством веса (сложить веса ребер).
Пример для $n=2$ две вершины А -<>-B вес 2.
Пример для $n=3$ три вершины А -> B -> C -> А - вес 3.
Пример для $n=4$ четыре вершины А -> B -> C -> D -> A - вес 4.
Пример для $n=5$ пять вершин А -<>- B -> C -<>- D -<>- E -<>-A - вес 9.

Есть ли формула расчета для $n$ вершин ? Хочу создать программу на Python но не знаю как строить расчеты.
Может машинное обучение подключить? Но данных мало, может на OEIS есть последовательность ?

 Профиль  
                  
 
 Re: Расчет веса графа
Сообщение07.01.2025, 14:34 
Заслуженный участник


07/08/23
1224
При $n = 5$ пример не работает, из $D$ в $C$ кратчайший путь длины 4.

 Профиль  
                  
 
 Re: Расчет веса графа
Сообщение07.01.2025, 14:57 
Аватара пользователя


12/10/16
643
Almaty, Kazakhstan
dgwuqtj
dgwuqtj в сообщении #1668914 писал(а):
При $n = 5$ пример не работает, из $D$ в $C$ кратчайший путь длины 4.

Да, просчитался, исправил.

 Профиль  
                  
 
 Re: Расчет веса графа
Сообщение07.01.2025, 14:58 
Заслуженный участник


07/08/23
1224
Если уж писать программу, то можно просто перебрать все графы на фиксированном множестве из $n$ вершин. Есть пример с $2 n - 3$ рёбрами при $n \geq 3$: соединим первые три вершины в ориентированный треугольник, а также соединим одну из них со всеми остальными неориентированными рёбрами. Всего графов с $\leq 2 n - 4$ рёбрами ровно $\binom {n^2 - n} 0 + \ldots + \binom{n^2 - n}{2 n - 4}$. При $n = 11$ эта сумма равна $669\,240\,664$, что даже на Питоне по идее перебирается.

Или есть основания предполагать, что там имеется красивая формула для произвольного $n$?

 Профиль  
                  
 
 Re: Расчет веса графа
Сообщение07.01.2025, 15:15 
Аватара пользователя


12/10/16
643
Almaty, Kazakhstan
dgwuqtj в сообщении #1668923 писал(а):
Или есть основания предполагать, что там имеется красивая формула для произвольного $n$?

Об этом я и интересуюсь, есть ли точная формула или как рассчитать без перебора.
dgwuqtj в сообщении #1668923 писал(а):
а потом первые 3 вершины соединим в ориентированный треугольник.

то есть больше одного цикла с тремя вершинами (и с ориентированными ребрами ) не получится построить ?

 Профиль  
                  
 
 Re: Расчет веса графа
Сообщение07.01.2025, 15:17 
Заслуженный участник
Аватара пользователя


03/06/08
2342
МО
Можно еще к mip свести и подтянуть, соответственно, какую-то библиотечку, типа or-tools.
Правда, реально вряд ли оно считать будет, в смысле, для сколько-то больших чисел.

 Профиль  
                  
 
 Re: Расчет веса графа
Сообщение07.01.2025, 15:44 
Заслуженный участник


07/08/23
1224
Soul Friend в сообщении #1668928 писал(а):
то есть больше одного цикла с тремя вершинами (и с ориентированными ребрами ) не получится построить ?

У меня не получилось. Я сначала думал, что у меня есть пример на $2 n - 4$ ребра, но там была ошибка.

 Профиль  
                  
 
 Re: Расчет веса графа
Сообщение07.01.2025, 15:55 
Аватара пользователя


12/10/16
643
Almaty, Kazakhstan
Soul Friend в сообщении #1668910 писал(а):
Пример для $n=5$ пять вершин А -<>- B -> C -<>- D -<>- E -<>-A - вес 9.

есть вес поменьше А -<>- B ; A -<>- C ; A -<>- D ; D -<>- E - общий вес 8.
для $n=6$ у меня получается наименьший вес 10.

 Профиль  
                  
 
 Re: Расчет веса графа
Сообщение07.01.2025, 17:28 
Заслуженный участник
Аватара пользователя


16/07/14
9255
Цюрих
Написал кривой код на ortools https://colab.research.google.com/drive ... sp=sharing.
Для $n = 5$ нашелся вес 7.
Вложение:
download (1).png
download (1).png [ 22.32 Кб | Просмотров: 0 ]

Для $n = 6$ - 9.
Вложение:
download (2).png
download (2).png [ 9.8 Кб | Просмотров: 92 ]

Но это собственно решение dgwuqtj, только вместо неориентированного ребра к центральной вершине, присоединяем новую к ребру треугольника.

Для $n = 7$, судя по скорости, за разумное время в текущем виде не посчитает, надо хоть немного оптимизировать.

 Профиль  
                  
 
 Re: Расчет веса графа
Сообщение07.01.2025, 22:42 
Аватара пользователя


12/10/16
643
Almaty, Kazakhstan
mihaild
Скормил ваш код в ChatGPT, теперь выводит результат в localhost через streamlit в 3D :
7 вершин посчитал быстро, а вот при расчете 8-ми вершин думал несколько минут.
3D он выводит быстро, а вот вывод в 2D тормозит и занимает время.
И вариантов могут быть несколько. У меня для 6 вершин с весом 9 получился другой граф.

(Код)

Код:
import streamlit as st
import networkx as nx
import numpy as np
from ortools.linear_solver import pywraplp
import plotly.graph_objects as go
import matplotlib.pyplot as plt

def calculate_and_draw(N):
    # Создание оптимизационной задачи
    solver = pywraplp.Solver.CreateSolver("SAT")

    # Переменные путей
    paths = {
        (i, j, p): solver.IntVar(0, 1, f"path{p}_{i}_{j}")
        for p in range(1, 4)
        for i in range(N)
        for j in range(N)
        if i != j
    }

    # Переменные для путей через вершину k
    ptk = {}
    for i in range(N):
        for j in range(N):
            if i != j:
                for p in [2, 3]:
                    path_through_k = {
                        k: solver.IntVar(0, 1, f"path{p}_{i}_{k}_{j}")
                        for k in range(N)
                        if k not in [i, j]
                    }
                    ptk[(i, j, p)] = path_through_k

                    # Добавляем ограничения
                    for k, v in path_through_k.items():
                        solver.Add(paths[(i, k, p - 1)] + paths[(k, j, 1)] >= 2 * v)
                    solver.Add(
                        paths[(i, j, p)]
                        <= sum(v for k, v in path_through_k.items())
                        + paths[(i, j, p - 1)]
                    )

                solver.Add(1 <= paths[(i, j, 3)])

    # Целевая функция: минимизация количества рёбер
    solver.Minimize(
        sum(paths[(i, j, 1)] for i in range(N) for j in range(N) if i != j)
    )

    # Решение задачи
    status = solver.Solve()
    if status != pywraplp.Solver.OPTIMAL:
        st.error("Не удалось найти оптимальное решение.")
        return

    # Вывод минимального веса
    min_weight = solver.Objective().Value()
    st.write(f"Минимальный вес: {min_weight}")

    # Построение графа на основе решения
    G = nx.DiGraph()

    # Добавляем вершины
    for i in range(N):
        G.add_node(i)

    # Списки рёбер для графики
    edges_oriented = []
    edges_bidirectional = []

    # Добавляем рёбра
    for i in range(N):
        for j in range(N):
            if i != j:
                # Ориентированные рёбра
                if paths[(i, j, 1)].solution_value() == 1:
                    G.add_edge(i, j)
                    edges_oriented.append((i, j))
                # Неориентированные рёбра (учитываем два направления)
                if paths.get((i, j, 1)) and paths.get((j, i, 1)):
                    if paths[(i, j, 1)].solution_value() == 1 and paths[(j, i, 1)].solution_value() == 1:
                        if (j, i) not in edges_bidirectional:
                            edges_bidirectional.append((i, j))

    # Генерация координат для вершин
    pos = {
        i: (
            np.cos(2 * np.pi * i / N),
            np.sin(2 * np.pi * i / N),
            0.5 * (i % 2),
        )
        for i in range(N)
    }

    # 3D график с использованием Plotly
    fig = go.Figure()

    # Отображение ориентированных рёбер (синим цветом с стрелками)
    for edge in edges_oriented:
        x = [pos[edge[0]][0], pos[edge[1]][0]]
        y = [pos[edge[0]][1], pos[edge[1]][1]]
        z = [pos[edge[0]][2], pos[edge[1]][2]]
        fig.add_trace(go.Scatter3d(
            x=x, y=y, z=z, mode='lines+text',
            line=dict(color='blue', width=4),
            text=[f"{edge[0]} → {edge[1]}"],
            textposition="top center"
        ))

    # Отображение неориентированных рёбер (зелёным цветом)
    for edge in edges_bidirectional:
        x = [pos[edge[0]][0], pos[edge[1]][0]]
        y = [pos[edge[0]][1], pos[edge[1]][1]]
        z = [pos[edge[0]][2], pos[edge[1]][2]]
        fig.add_trace(go.Scatter3d(
            x=x, y=y, z=z, mode='lines+text',
            line=dict(color='green', width=4),
            text=[f"{edge[0]} ↔ {edge[1]}"],
            textposition="top center"
        ))

    # Отображение вершин
    for node, (x, y, z) in pos.items():
        fig.add_trace(go.Scatter3d(
            x=[x], y=[y], z=[z], mode='markers+text',
            marker=dict(size=10, color='red'),
            text=[str(node)],
            textposition="bottom center"
        ))

    fig.update_layout(
        title="Минимальный граф с радиусом ≤ 3",
        scene=dict(
            xaxis_title='X',
            yaxis_title='Y',
            zaxis_title='Z'
        ),
        showlegend=False
    )

    # Отображение интерактивного 3D графика
    st.plotly_chart(fig)

    # 2D график с использованием NetworkX
    fig_2d, ax = plt.subplots(figsize=(8, 6))

    # Попробуем использовать стандартное положение для рёбер
    pos_2d = nx.circular_layout(G)  # Используем круговую раскладку для 2D

    # Построение 2D-графика
    nx.draw(G, pos_2d, with_labels=True, node_size=500, node_color='red', font_size=16, font_color='white', edge_color='blue', width=2, arrows=True)

    # Отображение 2D графика
    st.pyplot(fig_2d)


# Интерфейс Streamlit
st.title("Минимизация веса графа с ограничениями")

# Поле для ввода количества вершин
N = st.number_input("Введите количество вершин (N)", min_value=2, step=1)

# Кнопка для расчета
if st.button("Рассчитать"):
    calculate_and_draw(N)


 Профиль  
                  
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 10 ] 

Модераторы: Модераторы Математики, Супермодераторы



Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group