Как наложить ограничения на основе предыдущего решения в PuLP?

#python #optimization #mathematical-optimization #pulp #mixed-integer-programming

#python #оптимизация #математическая оптимизация #pulp #смешанное целочисленное программирование

Вопрос:

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

Мой вопрос в том, что я хочу повторить этот процесс многократно конечное число раз и каждый раз использовать решение для наложения дополнительных ограничений на следующий раз / раунд.

Цена колеблется каждый раз, поэтому необходимо будет упаковывать разные товары. Однако я разрешаю только одно бесплатное изменение при каждом запуске решателя. За каждое дополнительное изменение из последнего набора решений будет взиматься штраф, скажем, -100 за каждый элемент. Игрушечный пример: Итак, если последнее решение было [0,0,1,1,0,0,0,0,0], а новое решение [1,1,0,0,0,0,0,0,0], то в достижении цели -100 был бы наложен штраф из-за того, что они были 2 изменениями с последнего раунда. Если бы оно изменилось на [0,1,0,1,0,0,0,0,0,0], то это было бы непенализировано.

Как мне наложить это наказание в objective и наложить ограничение на 1 бесплатное изменение?

Начальная программа выглядит следующим образом:

 items = [i for i in range(len(df))]
price = df['price'].to_dict()
volume = df['volume'].to_dict() 
weight = df['weight'].to_dict()


prob = LpProblem('mip',LpMaximize)
x = LpVariable.dicts("items", items, 0, 1, LpBinary)

#objective
prob  = lpSum([(price[i]*x[i]) for i in items])

#constraints
prob  = lpSum([x[i] for i in items]) = 10
prob  = lpSum([weight[i] * x[i] for i in items]) <= 1000
prob  = lpSum([volume[i] * x[i] for i in items]) <= 5000

prob.solve()

#to get the solution in a list for reuse
current_solution = [x[i].varValue for i in items]
  

Я думал об использовании фиктивных элементов в var [i] с prices = -100, но не смог заставить его работать. Любая помощь? Заранее большое спасибо.

Ответ №1:

Не очень просто.

Я бы попробовал что-то вроде:

(1) Введите двоичную переменную d[i] ∈ {0,1} и ограничения:

  -d[i] <= x[i] - x0[i] <= d[i]
  

где x0 — предыдущее решение. (Это должно быть реализовано как два разных ограничения в PuLP. Кроме того, мы на самом деле можем расслабиться d , чтобы быть непрерывным между 0 и 1: он будет автоматически двоичным.)

(2) Добавьте переменную n и ограничение:

   n = sum(i, d[i])
  

(3) Добавьте положительную переменную n1 >= 0 и ограничение

   n1 >= n - 1 
  

(4) Добавьте термин к цели

  -100*n1
  

(мы хотим минимизировать 100*n1 , поэтому я добавил знак минус, поскольку ваш объект максимизируется).

Комментарии:

1. (1) Зачем вам коэффициент 2 или -2 для d[i]? x [i] — x0 [i] может быть только 1 — 1 или 0. (1-0), т. е. недавно выбранный, (0-1), т. е. был удален, (0-0), т. Е. не выбран снова или (1-1) выбран снова. (2) Я не уверен, какова цель n? Что в любом случае дает нам сумма i и d [i]? Спасибо

2. Как насчет (2)? Не совсем уверен, что следую логике..

3. (2) n подсчитывает количество различий.

4. Извините, я новичок в PuLP, и все это довольно запутанно. Итак, вы имеете в виду, что сумма (i, d [i]) в основном равна lpSum([d [i] для i в элементах]) в PuLP? И добавляются ли n и n1 следующими LpVariable.dicts («name», items, 0) Или они добавляются как постоянные переменные по существу?

5. Это в псевдокодовой (математической) нотации. Перевод на язык PuLP — это тривиально.