Вопрос или проблема
Не удается реализовать CVRPTW (Задача маршрутизации транспортных средств с ограниченной мощностью и временными окнами) в IBM ILOG CPLEX с адекватным решением.
Мой код .mod:
int n = ...; // Множество вершин.
int nVehicles = ...; // Количество транспортных средств.
range N = 0..n-1; // Диапазон всех точек (включая склад).
range C = 1..n-1; // Диапазон всех клиентов (исключая склад).
range V = 0..nVehicles-1; // Диапазон всех транспортных средств.
int c[N][N] = ...; // Транспортные расходы.
dvar boolean x[N][N][V]; // 1, если k-е транспортное средство движется от i к j, в противном случае 0.
dvar int+ Z[V];
int Q = ...; // Вместимость транспортного средства.
int d[C] = ...; // Спрос клиентов.
dvar int+ s[N][V]; // Время прибытия.
int t[N][N] = ...; // Время транспортировки.
int a[N] = ...; // Начало временных окон.
int b[N] = ...; // Конец временных окон.
// 1. Целевая функция: минимизировать общие транспортные расходы.
minimize sum(k in V) Z[k];
subject to {
// Все транспортные средства выезжают со склада в 0 времени.
//forall(k in V)
//s[0][k] == 0;
// 2. Каждый клиент посещается ровно один раз.
forall(i in C)
sum(k in V, j in N) x[i][j][k] == 1;
// 3. Ограничение по мощностям.
forall(k in V)
sum(i in C, j in N) (d[i] * x[i][j][k]) <= Q;
// 4. Каждое транспортное средство начинает свой маршрут со склада.
forall(k in V)
sum(j in N) x[0][j][k] == 1;
// 5. Передвижение между клиентами.
forall(k in V, h in C)
sum(i in N) x[i][h][k] == sum(j in N) x[h][j][k];
// 6. Каждое транспортное средство возвращается на склад.
forall(k in V)
sum(i in N) x[i][0][k] == 1; // Возможно 0 вместо n-1.
// 7. Время выезда из одной точки и время приезда в другую точку.
forall(i in N, j in N, k in V)
s[i][k] + t[i][j] <= s[j][k] + (1 - x[i][j][k]) * 10000; // Большое число для отключения ограничения, когда x[i][j][k] = 0.
// 8. Ограничения временных окон.
forall(i in N, k in V)
a[i] <= s[i][k] <= b[i];
// 10. Ограничение на использование указанного количества транспортных средств.
sum(k in V, j in N) x[0][j][k] <= nVehicles;
// Ограничение, связывающее переменную Z[k] с общими транспортными расходами
forall(k in V)
Z[k] == sum(i in N, j in N) (c[i][j] * x[i][j][k]);
}
Код .dat:
n = 6; // Множество вершин.
nVehicles = 4; // Количество транспортных средств.
// Транспортные расходы.
c = [
[0, 10, 15, 20, 30, 25],
[10, 0, 9, 14, 23, 18],
[15, 9, 0, 10, 12, 25],
[20, 14, 10, 0, 11, 15],
[30, 23, 12, 11, 0, 12],
[25, 18, 25, 15, 12, 0]
];
// Время транспортировки.
t = [
[0, 2, 3, 4, 5, 6],
[2, 0, 1, 3, 5, 7],
[3, 1, 0, 2, 4, 6],
[4, 3, 2, 0, 3, 5],
[5, 5, 4, 3, 0, 2],
[6, 7, 6, 5, 2, 0]
];
Q = 100; // Вместимость транспортного средства.
d = [2, 3, 4, 2, 1]; // Спрос.
// Временные окна
a = [0, 3, 5, 7, 9, 12];
b = [100, 10, 12, 14, 16, 18];
В результате я получаю нули в Z. Когда я меняю матрицу c диагонально на большие числа (например, 100000), я получаю 600000 100000 100000 100000.
Результаты, которые я хотел бы получить, должны выглядеть так:
Пример вывода
Предположим, у нас есть 4 транспортных средства и 5 клиентов. Результаты могут выглядеть так:
-
Маршрут для Транспортного Средства 1: Склад → Клиент 3 → Клиент 1 → Склад
-
Маршрут для Транспортного Средства 2: Склад → Клиент 2 → Клиент 4 → Клиент 5 → Склад
-
Маршрут для Транспортного Средства 3: Склад (не использовано)
-
Маршрут для Транспортного Средства 4: Склад (не использовано)
-
Общие расходы: 150 (например, общая дистанция или время маршрута)
-
Времена прибытия:
-
Транспортное средство 1: Клиент 3 — 5, Клиент 1 — 9
-
Транспортное средство 2: Клиент 2 — 4, Клиент 4 — 8, Клиент 5 — 12
-
Ответ или решение
Для реализации задачи CVRPTW (Capacitated Vehicle Routing Problem with Time Windows) в IBM ILOG CPLEX необходимо правильно настроить модель, учитывая ограничения, связанные с временем, вместимостью автомобилей и стоимостью маршрутов. Рассмотрим ключевые аспекты вашей модели и предложим необходимые корректировки.
Ошибки и недостатки в модели
-
Проблемы с определением переменной Z: Вам необходимо убедиться, что переменная Z действительно суммирует стоимость маршрута. Вы используете состояние
Z[k] == sum(i in N, j in N) (c[i][j] * x[i][j][k])
, что не будет работать, еслиx[i][j][k]
всегда 0. Рекомендуется использоватьZ[k] = sum(i in N, j in N) (c[i][j] * x[i][j][k])
как целевую функцию, а не как ограничение. -
Проблемы с обработкой времени ожидания: В вашем коде необходимо добавить ограничения, чтобы учесть время ожидания в рамках временных окон. Например, если доставляемый клиент не может быть обслужен до начала его временного окна
a[i]
, соответствующий временной параметр нужно корректировать. -
Сумма всех переменных x: Убедитесь, что вы правильно суммируете маршруты всех транспортных средств. Возможно, некоторые ограничения могут быть установлены неверно, что приводит к невозможности найти решение.
-
Обработка временных окон: Убедитесь, что временные ограничения были внедрены правильно. Конструкции типа
s[i][k] + t[i][j] <= s[j][k] + (1 - x[i][j][k]) * 10000
можно заменить условиями на строгое неравенство для несопоставимых маршрутов.
Корректировки в Коде
Вот исправленная версия вашего кода с соответствующими комментариями:
int n = ...; // Количество вершин.
int nVehicles = ...; // Количество автомобилей.
range N = 0..n-1; // Все точки (включая склад).
range C = 1..n-1; // Клиенты.
range V = 0..nVehicles-1; // Автомобили.
int c[N][N] = ...; // Стоимость перевозки.
dvar boolean x[N][N][V]; // 1, если k-й автомобиль идет от i к j.
dvar int+ Z[V]; // Стоимость маршрута для каждого автомобиля.
int Q = ...; // Вместимость автомобиля.
int d[C] = ...; // Спрос клиентов.
dvar int+ s[N][V]; // Время прибытия.
int t[N][N] = ...; // Время перевозки.
int a[N] = ...; // Начало временных окон.
int b[N] = ...; // Конец временных окон.
minimize sum(k in V) Z[k];
subject to {
// Ограничение по каждому клиенту
forall(i in C)
sum(k in V, j in N) x[i][j][k] == 1;
// Ограничение по вместимости
forall(k in V)
sum(i in C) d[i] * sum(j in N) x[i][j][k] <= Q;
// Ограничение на начальную точку
forall(k in V)
sum(j in N) x[0][j][k] == 1;
// Ограничение на возвращение в склад
forall(k in V)
sum(i in C) x[i][0][k] == 1;
// Ограничения на движение между клиентами
forall(k in V, h in C)
sum(i in N) x[i][h][k] == sum(j in N) x[h][j][k];
// Ограничения по времени
forall(i in N, j in N, k in V)
s[i][k] + t[i][j] <= s[j][k] + (1 - x[i][j][k]) * 10000;
// Ограничения по временным окнам
forall(i in N, k in V)
a[i] <= s[i][k] <= b[i];
// Ограничение по количеству автомобилей
sum(k in V) (sum(j in N) x[0][j][k]) <= nVehicles;
// Ограничение на стоимость
forall(k in V)
Z[k] == sum(i in N, j in N) (c[i][j] * x[i][j][k]);
}
Оптимизация решения
-
Тестирование с разными данными: Попробуйте запускать вашу модель с разными наборами данных, начиная с небольших. Это поможет вам идентифицировать ошибки в логике быстрее.
-
Логирование: При каждом вызове решения из CPLEX логируйте отладочную информацию, чтобы уточнить, какие условия о препятствиях мешают полному решению.
-
Оптимизация датасета: Убедитесь, что матрицы стоимости и время перевозки согласованы. Проверьте на наличие сатурированных (т.е. зависимых) узлов и постарайтесь оптимизировать или уменьшить их количество.
Изменения и улучшения, описанные выше, должны помочь вам решить задачу CVRPTW в IBM ILOG CPLEX. Убедитесь также, что ваша версия программного обеспечения и используемые библиотеки актуальны, так как это может повлиять на исполнение модели.