Оптимизация R максимальная покупка / продажа в зависимости от уровня запасов

Я хочу найти решение проблемы оптимизации. Цель состоит в том, чтобы максимизировать прибыль, покупая по низкой цене и продавая по более высокой. Существуют такие ограничения, как максимальный уровень запасов и максимальное количество покупок / продаж единиц. Кроме того, лимиты на продажу и покупку зависят от уровня запасов. Я задал аналогичный вопрос, хотя и без последнего условия здесь R-оптимизация, покупка, продажа.

Вот пример:

price = c(12, 11, 12, 13, 16, 17, 18, 17, 18, 16, 17, 13)
capacity = 25
max_units_buy_30 = 4 # when inventory level is lower then 30% it is possible to buy 0 to 4 units
max_units_buy_65 = 3 # when inventory level is between 30% and 65% it is possible to buy 0 to 3 units
max_units_buy_100 = 2 # when inventory level is between 65% and 100% it is possible to buy 0 to 2 units
max_units_sell_30 = 4 # when inventory level is lower then 30% it is possible to sell 0 to 4 units
max_units_sell_70 = 6 # when inventory level is between 30% and 70% it is possible to sell 0 to 6 units
max_units_sell_100 = 8 # when inventory level is between 70% and 100% it is possible to sell 0 to 8 units

person Jakub.Novotny    schedule 19.05.2020    source источник


Ответы (1)


Здесь много чего происходит.

  1. Описание

Похоже, в описании есть проблема. «Максимальная продажа / цена зависит от уровня запасов». Кажется, это неверно. Судя по данным, цена кажется постоянной, но ограничения на продажу и покупку зависят от уровня запасов.

  1. Время

Важно правильно выбрать время. Обычно мы рассматриваем buy и sell как события, происходящие в период t (мы называем их переменными потока). inv - это биржевая переменная, которая измеряется в конце периода t. Сказать, что sell[t] и buy[t] зависят от inv[t], немного странно (мы идем назад во времени). Конечно, мы можем его смоделировать и решить (мы решаем как одновременные уравнения, поэтому мы можем делать это). Но это может не иметь смысла в реальном мире. Вероятно, нам следует посмотреть на inv[t-1], чтобы изменить buy[t] и sell[t].

  1. Сегментирование уровней запасов.

Нам нужно разделить уровни запасов на сегменты. У нас есть следующие сегменты:

0%-30%
30%-65%
65%-70%
70%-100%

мы связываем двоичную переменную с каждым сегментом:

inventory in [0%-30%]  <=> δ[1,t] = 1, all other zero
             [30%-65%]     δ[2,t] = 1 
             [65%-70%]     δ[3,t] = 1 
             [70%-100%]    δ[4,t] = 1 

Поскольку нам нужно сделать это для всех периодов времени, мы добавляем дополнительный индекс t. Предупреждение: мы свяжем δ[k,t] с инвентарем в начале периода t, то есть inv[t-1]. Мы можем связать δ[k,t] с inv[t-1], изменив нижнюю и верхнюю границы в зависимости от того, в каком сегменте мы находимся.

  1. Границы на покупку / продажу

Как и в случае с ограничениями запасов, у нас есть следующие верхние границы для покупки и продажи:

     segment     buy   sell
     0%-30%       4     4 
     30%-65%      3     6
     65%-70%      2     6
     70%-100%     2     8

Первый шаг - разработать математическую модель. Здесь происходит слишком много всего, и мы можем сразу же все запрограммировать. Математическая модель - это наш «дизайн». Итак, начнем:

введите здесь описание изображения

Благодаря этому мы можем разработать некоторый код R. Здесь мы используем CVXR в качестве инструмента моделирования и GLPK в качестве решателя MIP.

> library(CVXR)
> 
> # data
> price = c(12, 11, 12, 13, 16, 17, 18, 17, 18, 16, 17, 13)
> capacity = 25
> max_units_buy = 4
> max_units_sell = 8
> 
> # capacity segments
> s <- c(0,0.3,0.65,0.7,1)
> 
> # corresponding lower and upper bounds
> invlb <- s[1:(length(s)-1)] * capacity
> invlb
[1]  0.00  7.50 16.25 17.50
> invub <- s[2:length(s)] * capacity
> invub
[1]  7.50 16.25 17.50 25.00
> 
> buyub <- c(4,3,2,2)
> sellub <- c(4,6,6,8)
> 
> # number of time periods
> NT <- length(price)
> NT
[1] 12
> 
> # number of capacity segments
> NS <- length(s)-1
> NS
[1] 4
> 
> # Decision variables
> inv = Variable(NT,integer=T)
> buy = Variable(NT,integer=T)
> sell = Variable(NT,integer=T)
> delta = Variable(NS,NT,boolean=T)
> 
> # Lag operator
> L = cbind(rbind(0,diag(NT-1)),0)
> 
> # optimization model
> problem <- Problem(Maximize(sum(price*(sell-buy))),
+                    list(inv == L %*% inv + buy - sell,
+                         sum_entries(delta,axis=2)==1, 
+                         L %*% inv >= t(delta) %*% invlb,
+                         L %*% inv <= t(delta) %*% invub,
+                         buy <= t(delta) %*% buyub,
+                         sell <= t(delta) %*% sellub,
+                         inv >= 0, inv <= capacity,
+                         buy >= 0, sell >= 0))
> result <- solve(problem,verbose=T)
GLPK Simplex Optimizer, v4.47
120 rows, 84 columns, 369 non-zeros
      0: obj =  0.000000000e+000  infeas = 1.200e+001 (24)
*    23: obj =  0.000000000e+000  infeas = 0.000e+000 (24)
*    85: obj = -9.875986758e+001  infeas = 0.000e+000 (2)
OPTIMAL SOLUTION FOUND
GLPK Integer Optimizer, v4.47
120 rows, 84 columns, 369 non-zeros
84 integer variables, 48 of which are binary
Integer optimization begins...
+    85: mip =     not found yet >=              -inf        (1; 0)
+   123: >>>>> -8.800000000e+001 >= -9.100000000e+001   3.4% (17; 0)
+   126: >>>>> -9.000000000e+001 >= -9.100000000e+001   1.1% (9; 11)
+   142: mip = -9.000000000e+001 >=     tree is empty   0.0% (0; 35)
INTEGER OPTIMAL SOLUTION FOUND
> cat("status:",result$status)
status: optimal
> cat("objective:",result$value)
objective: 90
> print(result$getValue(buy))
      [,1]
 [1,]    3
 [2,]    4
 [3,]    4
 [4,]    3
 [5,]    3
 [6,]    1
 [7,]    0
 [8,]    0
 [9,]    0
[10,]    4
[11,]    0
[12,]    0
> print(result$getValue(sell))
      [,1]
 [1,]    0
 [2,]    0
 [3,]    0
 [4,]    0
 [5,]    0
 [6,]    0
 [7,]    8
 [8,]    6
 [9,]    4
[10,]    0
[11,]    4
[12,]    0
> print(result$getValue(inv))
      [,1]
 [1,]    3
 [2,]    7
 [3,]   11
 [4,]   14
 [5,]   17
 [6,]   18
 [7,]   10
 [8,]    4
 [9,]    0
[10,]    4
[11,]    0
[12,]    0
> print(result$getValue(delta))
     [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12]
[1,]    1    1    1    0    0    0    0    0    1     1     1     1
[2,]    0    0    0    1    1    0    0    1    0     0     0     0
[3,]    0    0    0    0    0    1    0    0    0     0     0     0
[4,]    0    0    0    0    0    0    1    0    0     0     0     0
> 

Так что, думаю, кто-то должен мне за это хорошую бутылку коньяка.

person Erwin Kalvelagen    schedule 20.05.2020
comment
Большое спасибо. Я очень ценю усилия. Однако код, похоже, не работает на моем ноутбуке. cat (status:, result $ status) дает мне статус :feasible_inaccurate. Вы можете придумать причину, почему? Вот моя версия sessionInfo () R 4.0.0 (2020-04-24) Платформа: x86_64-w64-mingw32 / x64 (64-разрядная) Работает под: Windows 10 x64 (сборка 18362) Матричные продукты: локаль по умолчанию: [1 ] LC_COLLATE = Czech_Czechia.1250 LC_CTYPE = Czech_Czechia.1250 LC_MONETARY = Czech_Czechia.1250 LC_NUMERIC = C LC_TIME = Czech_Czechia.1250 - person Jakub.Novotny; 20.05.2020
comment
Я понял - мне нужен пакет Rglpk. - person Jakub.Novotny; 20.05.2020
comment
Чтобы вы знали, я также отредактировал свой исходный вопрос, чтобы исправить ошибку в описании, которое вы указали (смешение между ценой покупки / продажи и количеством покупки / продажи). Что касается бутылки хорошего коньяка, я предлагаю своему работодателю воспользоваться вашими консультационными услугами от stillanothermathprogrammingconsultant.blogspot.com < / а> - person Jakub.Novotny; 20.05.2020
comment
На самом деле это была небольшая интересная проблема. Теперь, если цена будет меняться вместе с уровнем запасов, это будет немного сложнее. - person Erwin Kalvelagen; 21.05.2020