В современной логистике часто возникает задача оптимального распределения заказов по транспортным средствам. Типичный пример: необходимо сгруппировать пункты назначения так, чтобы в один рейс попадало не более 3 точек, а суммарный объем груза составлял около 30 кубических метров. При этом важно соблюдать направление (кластеризацию) и минимизировать «пустые» пробеги.
В рамках данной статьи мы разберем, как подступиться к решению этой задачи в среде 1С, рассмотрим математическую подоплеку и проанализируем различные стратегии — от простых эвристик до интеграции с мощными математическими движками.
С технической точки зрения описанная проблема относится к классу NP-трудных комбинаторных задач, а именно к задаче о бункере (Bin Packing Problem) с дополнительным ограничением на количество элементов (Cardinality Constraint). Попытка решить её простым перебором (цикл в цикле) на больших объемах данных (например, более 50–100 заказов) приведет к экспоненциальному росту времени расчета, что сделает работу системы крайне медленной.
Проанализируем основные ограничения, которые нам необходимо учитывать:
Если бюджет проекта не позволяет использовать внешние сервисы, мы можем реализовать алгоритм First Fit Decreasing (FFD). Рассмотрим этот метод подробнее: сначала мы сортируем все заказы внутри кластера по убыванию объема, а затем последовательно пытаемся «упаковать» их в существующие или новые рейсы.
Давайте посмотрим на пример логики, которую можно реализовать в коде 1С:
// Пример упрощенного распределения заказов по алгоритму FFD
Процедура РаспределитьЗаказыПоРейсам(ТаблицаЗаказов)
// 1. Сортируем таблицу по Кластеру и по Объему (убывание)
ТаблицаЗаказов.Сортировать("Кластер Возр, Объем Убыв");
Рейсы = Новый ТаблицаЗначений;
Рейсы.Колонки.Добавить("НомерРейса");
Рейсы.Колонки.Добавить("ОбщийОбъем");
Рейсы.Колонки.Добавить("КоличествоТочек");
Для Каждого СтрокаЗаказа Из ТаблицаЗаказов Цикл
Упаковано = Ложь;
// Пытаемся найти подходящий существующий рейс
Для Каждого Рейс Из Рейсы Цикл
Если Рейс.ОбщийОбъем + СтрокаЗаказа.Объем <= 30
И Рейс.КоличествоТочек < 3 Тогда
// Добавляем заказ в этот рейс
Рейс.ОбщийОбъем = Рейс.ОбщийОбъем + СтрокаЗаказа.Объем;
Рейс.КоличествоТочек = Рейс.КоличествоТочек + 1;
СтрокаЗаказа.НомерРейса = Рейс.НомерРейса;
Упаковано = Истина;
Прервать;
КонецЕсли;
КонецЦикла;
// Если не влезло ни в один рейс, создаем новый
Если Не Упаковано Тогда
НовыйРейс = Рейсы.Добавить();
НовыйРейс.НомерРейса = Рейсы.Количество();
НовыйРейс.ОбщийОбъем = СтрокаЗаказа.Объем;
НовыйРейс.КоличествоТочек = 1;
СтрокаЗаказа.НомерРейса = НовыйРейс.НомерРейса;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Этот метод работает быстро и дает результат, близкий к оптимальному. Однако он не учитывает последовательность объезда точек, что подводит нас к следующему этапу оптимизации.
Как показывает практика, часто наиболее эффективным решением для 1С является создание Рабочего места (АРМ), где система делает предварительную группировку, а логист финально утверждает рейсы. Выясним причину популярности такого подхода: реальная жизнь часто подкидывает условия, которые сложно описать математически (ремонт дорог, габариты груза, личные предпочтения водителей).
Рассмотрим структуру такого АРМ:
30 кубов превышен, система выдает предупреждение.Если задача требует максимально плотной загрузки транспорта и учета расстояний, стоит рассмотреть интеграцию с внешними сервисами. Проанализируем ситуацию: зачем изобретать велосипед, если существуют готовые облачные решения?
Популярные сервисы для интеграции:
HTTPЗапрос и возвращать идеальное распределение, решая задачу линейного программирования.Пример схемы взаимодействия с внешним сервисом из 1С:
// Пример отправки данных на внешний сервис оптимизации
Функция ПолучитьОптимальныеРейсы(ДанныеЗаказов)
Запрос = Новый HTTPЗапрос("/v1/optimize");
Запрос.УстановитьТелоИзСтроки(СформироватьJSON(ДанныеЗаказов));
Соединение = Новый HTTPСоединение("api.logistics-service.com");
Ответ = Соединение.ОтправитьДляОбработки(Запрос);
Если Ответ.КодСостояния = 200 Тогда
Возврат ДесериализоватьJSON(Ответ.ПолучитьТелоКакСтроку());
КонецЕсли;
КонецФункции
Для небольших компаний интересным вариантом может стать использование возможностей Microsoft Excel. В Excel встроен мощный инструмент «Поиск решения» (Solver), который умеет решать задачи линейного программирования. Программист 1С может выгрузить данные в Excel через COMОбъект, запустить макрос поиска решения и забрать результат обратно в 1С.
Этот путь сложен в поддержке (требует установленного Excel на сервере/клиенте), но позволяет решать задачи оптимизации без покупки дорогостоящих TMS-систем.
Подводя итог, при выборе способа решения логистической задачи в 1С, мы рекомендуем придерживаться следующего плана:
3 точек и не более 30 кубов. Это предотвратит ошибки человеческого фактора.ФоновыеЗадания, чтобы не блокировать работу пользователя.1С:ERP и 1С:Управление торговлей 11 уже есть встроенные механизмы планирования доставки. Прежде чем писать свое решение, проанализируйте возможности подсистемы «Доставка».Выбор конкретного метода зависит от размерности ваших данных и требований к точности. Для малого бизнеса достаточно удобного АРМ с флажками, для крупного дистрибьютора с сотнями машин — необходима интеграция с математическими решателями или полноценной TMS.