Когда Дейкстра бессилен: алгоритм Беллмана-Форда для 1С - поиск кратчайших путей с отрицательными весами. Решаем задачу поиска маршрута с прибылью (код внутри)

26.03.25

Разработка - Математика и алгоритмы

Узнайте, как использовать алгоритм Беллмана-Форда в 1С для решения задач оптимизации маршрутов, анализа финансовых потоков и поиска арбитражных возможностей. В статье представлен готовый к использованию код и примеры применения.

Скачать файл

ВНИМАНИЕ: Файлы из Базы знаний - это исходный код разработки. Это примеры решения задач, шаблоны, заготовки, "строительные материалы" для учетной системы. Файлы ориентированы на специалистов 1С, которые могут разобраться в коде и оптимизировать программу для запуска в базе данных. Гарантии работоспособности нет. Возврата нет. Технической поддержки нет.

Наименование Бесплатно
Когда Дейкстра бессилен: Алгоритм Беллмана-Форда для 1С поиск кратчайших путей с отрицательными весами. Решаем задачу поиска маршрута с прибылью (код внутри):
.epf 6,89Kb
7
7 Скачать бесплатно

Беллман-Форд в 1С: Оптимизация с отрицательными весами

Статья посвящена реализации алгоритма Беллмана-Форда на платформе 1С 8.3. Рассматривается применение алгоритма для поиска кратчайших путей в графах с ребрами, имеющими отрицательный вес. Приведен пример кода и показаны практические сценарии использования.

В задачах оптимизации маршрутов часто требуется найти кратчайший путь между двумя точками. Для решения таких задач существует множество алгоритмов, одним из самых известных является алгоритм Дейкстры. Однако алгоритм Дейкстры не работает с графами, содержащими ребра с отрицательным весом. В таких случаях на помощь приходит алгоритм Беллмана-Форда. В данной статье мы рассмотрим реализацию алгоритма Беллмана-Форда на платформе 1С 8.3, покажем, как его можно использовать для решения практических задач, и приведем пример кода.

Что такое алгоритм Беллмана-Форда?

Алгоритм Беллмана-Форда – это алгоритм поиска кратчайшего пути во взвешенном графе. В отличие от алгоритма Дейкстры, он может работать с графами, содержащими ребра с отрицательным весом. Алгоритм также позволяет определить, есть ли в графе цикл отрицательного веса (цикл, сумма весов ребер которого отрицательна), наличие которого делает задачу поиска кратчайшего пути бессмысленной.

Алгоритм Беллмана-Форда работает путем последовательной релаксации ребер графа. Релаксация ребра – это процесс обновления оценки кратчайшего пути до конечной вершины ребра, если найден более короткий путь через начальную вершину ребра. Алгоритм выполняет |V| - 1 итераций (где |V| – количество вершин в графе), на каждой итерации релаксируя все ребра графа. После |V| - 1 итераций, если после очередной релаксации ребер расстояние до какой-либо вершины уменьшилось, значит, в графе есть цикл отрицательного веса.

Практическое применение алгоритма Беллмана-Форда:

Алгоритм Беллмана-Форда может быть использован для решения различных задач, в которых требуется найти кратчайший путь во взвешенном графе, содержащем ребра с отрицательным весом. Вот несколько примеров:

  • Финансовый анализ: Поиск арбитражных возможностей на валютном рынке. В этом случае вершины графа представляют валюты, а ребра – обменные курсы. Отрицательный вес ребра может соответствовать комиссии за обмен.
  • Оптимизация маршрутов с учетом скидок и штрафов: В задачах логистики и доставки отрицательный вес может представлять скидку на транспортировку между определенными пунктами.
  • Анализ сетевых протоколов: Поиск кратчайших путей в сетях с переменной пропускной способностью, где отрицательный вес может представлять увеличение пропускной способности.

Ниже приведен пример реализации алгоритма Беллмана-Форда на платформе 1С 8.3. В данном примере граф задается программно в виде соответствия, где ключи – это вершины (города), а значения – другие соответствия, представляющие ребра, исходящие из данной вершины, и их веса (расстояния).

&НаСервере
Функция НайтиКратчайшийПутьБеллманаФорда(Граф, НачальныйГород, КонечныйГород, Бесконечность)

    // 1. Инициализация
    distances = Новый Соответствие();
    previous = Новый Соответствие();

    Для Каждого ЭлементГрафа Из Граф Цикл
        город = ЭлементГрафа.Ключ;
        distances.Вставить(город, Число(Бесконечность));
        previous.Вставить(город, "");
    КонецЦикла;

    distances[НачальныйГород] = 0;

    // 2. Основной цикл алгоритма (релаксация ребер)
    Для Индекс = 1 По Граф.Количество() - 1 Цикл

        Для Каждого ЭлементГрафа Из Граф Цикл
            sourceCity = ЭлементГрафа.Ключ;

            Для Каждого Сосед Из Граф[sourceCity] Цикл
                destinationCity = Сосед.Ключ;
                weight = Сосед.Значение;

                Если distances[sourceCity] <> Число(Бесконечность) И distances[sourceCity] + weight < distances[destinationCity] Тогда
                    distances[destinationCity] = distances[sourceCity] + weight;
                    previous[destinationCity] = sourceCity;
                КонецЕсли;
            КонецЦикла;
        КонецЦикла;
    КонецЦикла;

    // 3. Проверка наличия цикла отрицательного веса
    Для Каждого ЭлементГрафа Из Граф Цикл
        sourceCity = ЭлементГрафа.Ключ;

        Для Каждого Сосед Из Граф[sourceCity] Цикл
            destinationCity = Сосед.Ключ;
            weight = Сосед.Значение;

            Если distances[sourceCity] <> Число(Бесконечность) И distances[sourceCity] + weight < distances[destinationCity] Тогда
                // Обнаружен цикл отрицательного веса
                Возврат Неопределено;
            КонецЕсли;
        КонецЦикла;
    КонецЦикла;

    // 4. Восстановление пути
    Путь = Новый Массив();
    currentCity = КонечныйГород;

    Пока currentCity <> "" Цикл
        Путь.Вставить(1, currentCity);
        currentCity = previous[currentCity];
    КонецЦикла;

    // 5. Формируем строку пути
	СтрокаПути = СтрСоединить(Путь, " -> ");

    // 6. Возвращаем результаты
    Результаты = Новый Структура("Путь, Длина", СтрокаПути, distances[КонечныйГород]);
    Возврат Результаты;

КонецФункции

Вспомогательный код для запуска алгоритма (процедура обработки команды на форме):

&НаСервере
Процедура ЗапуститьАлгоритмНаСервере()
	// 0. Определяем константу "Бесконечность"
    Бесконечность = 1000000; // или другое достаточно большое число

    // 1. Задаем граф (программно)
    Граф = Новый Соответствие();

    // Добавляем вершины (города)
    Граф.Вставить("Город1", Новый Соответствие());
    Граф.Вставить("Город2", Новый Соответствие());
    Граф.Вставить("Город3", Новый Соответствие());
    Граф.Вставить("Город4", Новый Соответствие());

    // Добавляем ребра (расстояния между городами)
    // Внимание! Добавляем ребро с отрицательным весом!
    Граф["Город1"].Вставить("Город2", 10);
    Граф["Город1"].Вставить("Город3", 15);
    Граф["Город2"].Вставить("Город1", 10);
    Граф["Город2"].Вставить("Город3", 8);
    Граф["Город2"].Вставить("Город4", 20);
    Граф["Город3"].Вставить("Город1", 15);
    Граф["Город3"].Вставить("Город2", 8);
    Граф["Город3"].Вставить("Город4", 5);
    Граф["Город4"].Вставить("Город2", 20);
    Граф["Город4"].Вставить("Город3", -5); // Отрицательный вес!

    // 2. Указываем начальный и конечный город
    НачальныйГород = "Город1";
    КонечныйГород = "Город4";

    // 3. Запускаем алгоритм Беллмана-Форда
    Результаты = НайтиКратчайшийПутьБеллманаФорда(Граф, НачальныйГород, КонечныйГород, Бесконечность);

    // 4. Выводим результаты в окно сообщений
    Если Результаты <> Неопределено Тогда
        Сообщить("Кратчайший путь: " + Результаты.Путь);
        Сообщить("Длина пути: " + Результаты.Длина);
    Иначе
        Сообщить("Путь из " + НачальныйГород + " в " + КонечныйГород + " не найден или обнаружен цикл отрицательного веса.");
    КонецЕсли;	
КонецПроцедуры

&НаКлиенте
Процедура ЗапуститьАлгоритм(Команда)
	ЗапуститьАлгоритмНаСервере();
КонецПроцедуры  

Описание кода:

  1. Функция НайтиКратчайшийПутьБеллманаФорда принимает на вход граф, начальный город, конечный город и значение “бесконечности”.
  2. Функция инициализирует расстояния до всех городов значением “бесконечности”, а расстояние до начального города – нулем.
  3. Основной цикл алгоритма выполняется |V| - 1 раз, где |V| – количество городов.
  4. Внутри цикла происходит релаксация всех ребер графа.
  5. После завершения основного цикла происходит проверка наличия цикла отрицательного веса.
  6. Если цикл отрицательного веса не обнаружен, функция восстанавливает кратчайший путь и возвращает его и его длину.
  7. Если цикл отрицательного веса обнаружен, функция возвращает Неопределено

Почему расстояние может быть -5?

Очень важный вопрос! Действительно, понятие “расстояние” в обычном понимании не может быть отрицательным. Расстояние – это физическая величина, характеризующая протяжённость между двумя точками, и она всегда неотрицательна.

Однако, в контексте алгоритма Беллмана-Форда, отрицательные веса ребер графа могут использоваться для моделирования различных ситуаций, не связанных напрямую с физическим расстоянием. Вот несколько примеров:

  1. Прибыль/Убыток: В задачах финансового планирования, вес ребра может представлять не расстояние, а прибыль (положительное значение) или убыток (отрицательное значение) от перехода из одного состояния в другое. Алгоритм Беллмана-Форда в этом случае будет искать путь, максимизирующий прибыль (или минимизирующий убытки).

  2. Скидки/Штрафы: В задачах логистики и доставки, отрицательный вес может представлять скидку на транспортировку между определенными пунктами, а положительный вес - штраф.

  3. Моделирование изменений: Отрицательные веса можно использовать для моделирования изменений в системе. Например, если алгоритм используется для планирования производства, отрицательный вес может представлять уменьшение запасов сырья при переходе от одного этапа производства к другому.

  4. Обнаружение арбитража: В финансовых приложениях, алгоритм Беллмана-Форда может использоваться для обнаружения возможностей арбитража на валютном рынке. Отрицательный цикл в графе будет указывать на возможность получить прибыль, конвертируя валюту по определенному кругу.

Важно помнить:

  • Алгоритм Дейкстры не работает с отрицательными весами, так как он предполагает, что добавление новых ребер всегда увеличивает длину пути.
  • Алгоритм Беллмана-Форда позволяет обрабатывать отрицательные веса, но он также способен обнаруживать циклы отрицательного веса (циклы в графе, сумма весов ребер которых отрицательна). Наличие такого цикла означает, что не существует кратчайшего пути (потому что можно бесконечно “наматывать” круги по этому циклу, каждый раз уменьшая длину пути).

В нашем примере с городами, отрицательное “расстояние” между городами 4 и 3 можно интерпретировать, например, как доплату за проезд по этому маршруту (вместо платы за проезд). То есть, если вы проедете из города 4 в город 3, вы получите 5 единиц (например, валюты) вместо того, чтобы платить. Это, конечно, не физическое расстояние, но вполне допустимая модель для алгоритма Беллмана-Форда.

Алгоритм Беллмана-Форда – это мощный инструмент для поиска кратчайших путей во взвешенном графе, который может работать с ребрами, имеющими отрицательный вес. В данной статье мы рассмотрели реализацию алгоритма Беллмана-Форда на платформе 1С 8.3 и показали, как его можно использовать для решения практических задач. Приведенный пример кода может быть использован в качестве основы для разработки более сложных и специализированных решений.

 

Тестирование и настройка:

Алгоритм Беллмана-Форда, представленный в этой статье, был протестирован на платформе 1С:Предприятие 8.3 (версия 8.3.23.1862). Для тестирования использовалась форма внешней обработки.

Настройка данных для тестирования:

Для изменения данных, используемых алгоритмом, необходимо внести изменения в процедуру ЗапуститьАлгоритмНаСервере(). В этой процедуре программно задается граф, начальный и конечный города.

  • Изменение городов и расстояний: В разделе // 1. Задаем граф (программно) можно изменить список городов и расстояния между ними. 
    // Добавляем вершины (города)
    Граф.Вставить("Город1", Новый Соответствие());
    Граф.Вставить("Город2", Новый Соответствие());
    Граф.Вставить("Город3", Новый Соответствие());
    Граф.Вставить("Город4", Новый Соответствие());
    
    // Добавляем ребра (расстояния между городами)
    // Внимание! Добавляем ребро с отрицательным весом!
    Граф["Город1"].Вставить("Город2", 10);
    Граф["Город1"].Вставить("Город3", 15);
    Граф["Город2"].Вставить("Город1", 10);
    Граф["Город2"].Вставить("Город3", 8);
    Граф["Город2"].Вставить("Город4", 20);
    Граф["Город3"].Вставить("Город1", 15);
    Граф["Город3"].Вставить("Город2", 8);
    Граф["Город3"].Вставить("Город4", 5);
    Граф["Город4"].Вставить("Город2", 20);
    Граф["Город4"].Вставить("Город3", -5); // Отрицательный вес!
  • Для добавления нового города, добавьте строку Граф.Вставить("НовыйГород", Новый Соответствие());. Для добавления расстояния между городами, добавьте строку Граф["Город1"].Вставить("Город2", 25);, где "Город1" – начальный город, "Город2" – конечный город, а 25– расстояние между ними. Помните: расстояния надо указывать в обе стороны, если это необходимо.
  • Изменение начального и конечного города: В разделе // 2. Указываем начальный и конечный город можно изменить начальный и конечный город для поиска кратчайшего пути.
    // 2. Указываем начальный и конечный город
    НачальныйГород = "Город1";
    КонечныйГород = "Город4";

    Просто измените значения переменных НачальныйГород  и КонечныйГород  на желаемые.

При изменении данных убедитесь, что все города, указанные в расстояниях, существуют в списке городов. В противном случае алгоритм может работать некорректно. Также, для наглядности, вы можете добавить вывод графа в окно сообщений перед запуском алгоритма.

Изменяя эти параметры, вы сможете протестировать алгоритм на различных сценариях и убедиться в его правильной работе.

Когда Дейкстра бессилен: Алгоритм Беллмана-Форда для с отрицательными весами

См. также

Математика и алгоритмы Программист Платформа 1C v8.2 1C:Бухгалтерия Россия Абонемент ($m)

На написание данной работы меня вдохновила работа @glassman «Переход на ClickHouse для анализа метрик». Автор анализирует большой объем данных, много миллионов строк, и убедительно доказывает, что ClickHouse справляется лучше PostgreSQL. Я же покажу как можно сократить объем данных в 49.9 раз при этом: 1. Сохранить значения локальных экстремумов 2. Отклонения от реальных значений имеют наперед заданную допустимую погрешность.

1 стартмани

30.01.2024    6350    stopa85    12    

39

Математика и алгоритмы Бесплатно (free)

Разработка алгоритма, построенного на модели симплекс-метода, для нахождения оптимального раскроя.

19.10.2023    11908    user1959478    56    

37

Математика и алгоритмы Разное Платформа 1С v8.3 1C:Бухгалтерия Россия Абонемент ($m)

Расширение (+ обработка) представляют собою математический тренажер. Ваш ребенок сможет проверить свои знание на математические вычисление до 100.

2 стартмани

29.09.2023    6141    maksa2005    8    

26

Математика и алгоритмы Инструментарий разработчика Программист Платформа 1С v8.3 Мобильная платформа Россия Абонемент ($m)

Что ж... лучше поздно, чем никогда. Подсистема 1С для работы с регулярными выражениями: разбор выражения, проверка на соответствие шаблону, поиск вхождений в тексте.

1 стартмани

09.06.2023    14306    8    SpaceOfMyHead    20    

63

Математика и алгоритмы Программист Платформа 1С v8.3 1C:Бухгалтерия Бесплатно (free)

Три задачи - три идеи - три решения. Мало кода, много смысла. Мини-статья.

03.04.2023    7398    RustIG    9    

28

Механизмы платформы 1С Математика и алгоритмы Программист Платформа 1С v8.3 Россия Бесплатно (free)

В статье анализируются средства платформы для решения системы линейных уравнений в 1С. Приводятся доводы в пользу некорректной работы встроенных алгоритмов, а значит потенциально некорректного расчета себестоимости в типовых конфигурациях.

23.11.2022    6552    gzharkoj    14    

25

Математика и алгоритмы Программист Платформа 1С v8.3 Россия Абонемент ($m)

Обычно под распределением понимают определение сумм пропорционально коэффициентам. Предлагаю включить сюда также распределение по порядку (FIFO, LIFO) и повысить уровень размерности до 2-х. 1-ое означает, что распределение может быть не только пропорциональным, но и по порядку, а 2-ое - это вариант реализации матричного распределения: по строкам и столбцам. Возможно вас заинтересует также необычное решение этой задачи через создание DSL на базе реализации текучего интерфейса

1 стартмани

21.03.2022    9749    7    kalyaka    11    

45
Оставьте свое сообщение