#mathematical-optimization
#математическая оптимизация
Вопрос:
У меня есть X количество деталей с фиксированной стоимостью. Каждая часть имеет N аспектов разной ценности.
Например:
Part 1: cost = 3,
Width = 10,
Length = 12,
Height = 13,
Weight = 12,
Value = sum of four aspects = 47
Part 2: cost 4,
Width = 20,
Length = 15,
Height = 12,
Weight = 10,
Value = sum of above four aspects = 57
Part 3: cost 2,
Width = 9,
Length = 12,
Height = 9,
Weight = 8,
Value = sum of above four aspects = 38
Затем у меня есть много таких разных предметов, например, 20 предметов.
Существует ограничение на максимальное количество предметов, которые я могу выбрать одновременно, например, 8 предметов.
У меня есть ограничение затрат, то есть сумма стоимости выбранных товаров должна быть с заданным пределом, например, 30.
Теперь мне нужно выбрать элементы из этого списка, чтобы получить максимальную ценность. Другим ограничением является то, что в конечном сочетании все аспекты должны быть сбалансированы. например, конечная сумма ширины, длины, высоты и веса должна быть распределена поровну. Если не равны, то, по крайней мере, в сбалансированном диапазоне. т. е. Поскольку существует 4 аспекта, каждый аспект должен составлять около 25% /-5% от общего значения.
Я попытался решить проблему с рюкзаком, но здесь у меня больше измерений.
Ответ №1:
Один из способов — использовать MILP-модель. Вот простая модель, реализованная в MiniZinc. x
это двоичные переменные, представляющие, выбран элемент или нет.
int: items = 5;
int: selectedItems = 3;
int: maxCost = 10;
set of int: ITEM = 1..items;
array[ITEM] of int: cost = [3, 4, 2, 1, 1];
array[ITEM] of int: width = [10, 20, 9, 15, 12];
array[ITEM] of int: length = [12, 15, 12, 15, 12];
array[ITEM] of int: height = [13, 12, 9, 15, 12];
array[ITEM] of int: weight = [12, 10, 8, 15, 12];
array[ITEM] of int: value = [width[i] length[i] height[i] weight[i] | i in ITEM];
array[ITEM] of var 0..1: x;
% selected items constraint
constraint sum(x) <= selectedItems;
% cost constraint
constraint sum(i in ITEM)(cost[i]*x[i]) <= maxCost;
predicate balanced(array[ITEM] of var int: values) =
let {var int: value = sum(i in ITEM)(values[i]*x[i])} in
value >= 0.2*totalValue / value <= 0.3*totalValue;
% balance constraints
constraint balanced(width) / balanced(length) / balanced(height) / balanced(weight);
var 1..sum(value): totalValue = sum(i in ITEM)(value[i]*x[i]);
solve maximize totalValue;
output ["totalValue=(totalValue)n"]
["cost=(sum(i in ITEM)(cost[i]*x[i]))n"]
["x="] [show(x)]
["nratio="] [show_float(5, 2,sum(i in ITEM)(width[i]*x[i]) / totalValue), show_float(5, 2,sum(i in ITEM)(length[i]*x[i]) / totalValue), show_float(5, 2,sum(i in ITEM)(height[i]*x[i]) / totalValue), show_float(5, 2,sum(i in ITEM)(weight[i]*x[i]) / totalValue)]
["nvalue="] [show(value)];
Запуск (с решателем Gecode по умолчанию) дает:
totalValue=165
cost=6
x=[0, 1, 0, 1, 1]
ratio= 0.28 0.25 0.24 0.22
value=[47, 57, 38, 60, 48]
Комментарии:
1. Спасибо. Это помогло решить мою первую проблему. Я действительно ничего не знал о MiniZinc, но его было довольно легко настроить, понять и запустить код. Сейчас я пытаюсь получить что-то вроде: установить одно значение на 30% и равномерно распределить другое. Как мне этого добиться? Я пытался работать с предикатом balanced и уравнением там, но, похоже, я не понял это правильно. Был бы признателен за любую помощь.
2. Вы могли бы рассмотреть возможность изменения вашей цели на что-то вроде (предполагая, что здесь вы хотите
width
приблизиться к 30%)solve maximize totalValue k*abs(sum(i in ITEM)(width[i]*x[i]));
, вотk
вес, используйте, например10
.