Začíname S YALMIPom: 04. Všeobecný distribučný problém

V predchádzajúcej časti sme videli ako naformulovať distribučný problém s 2 výrobcami a 2 zákazníkmi. Teraz úlohu naformulujeme pre ľubovolný počet výrobcov a zákazníkov.

Uvažujeme, že máme $n$ výrobcov, pričom $i$-tý výrobca vie vyrobiť maximálne $p_i$ produktov pre $i=1, \ldots, n$. Každý zákazník pritom požaduje celkovo $s_j$ produktov pre $j=1, \ldots, m$. Produkty sa prepravujú po cestách, pričom konštanty $c_{i,j}$ vyjadrujú jednotkovú cenu prepravy od $i$-teho výrobcu k $j$-temu zákazníkovi. Cieľom je nájsť optimalnú alokáciu dopravných ciest tak, aby boli dodržané výrobné kapacity a boli splnené požiadavky všetkých zákazníkov. Taktiež uvažujeme, že po jednotlivých cestách môžeme prepraviť iba nezáporné množstvá (nie každá cesta však musí byť využitá).

Matematicky sa dá takýto problém naformulovať nasledovne:

  • Optimalizovanou premennou je matica $x \in \mathbb{R}^{n \times m}$, ktorá ma $n$ riadkov (výrobcovia) a $m$ stĺpcov (zákazníci) a $x_{i,j}$ je množstvo tovarov prepravených od $i$-teho výrobcu $j$-temu zákazníkovi.
  • Účelová funkcia je v tvare $\min \ \sum_{i=1}^n \sum_{j=1}^m \ c_{i,j} x_{i,j}$, kde minimalizujeme celkovú cenu prepravy medzi jednotlivými výrobcami a zákazníkmi
  • Pre každého výrobcu chceme, aby jeho produkcia neprekročila $s_i$. Keďže produkcia je sumou vystupujúcich prúdov, dostávame ohraničenia v tvare nerovností: $\sum_{j=1}^m x_{i,j} \le p_i$ pre každé $i=1, \ldots, n$.
  • Každý zákazník musí dostať správne množstvo produktov, teda $\sum_{i=1}^n x_{i,j} = s_j$ pre každé $j=1, \ldots, m$.
  • Prepravovať môžeme iba nezáporné množstvá, teda $x_{i,j} \ge 0$ pre všetky $i=1, \ldots, n$ a $j=1, \ldots, m$.

V YALMIPe takýto problém zapíšeme veľmi jednoducho. Najskôr vytvoríme optimalizovanú premennú, ktorá je v našom prípade maticou s $n$ riadkami a $m$ stĺpcami:

x = sdpvar(n, m, 'full')

Argument 'full' musíme pridať, aby YALMIP vedel, že nejde o symetrickú maticu.

Následne vytvoríme účelovú funkciu. Najjednoduhšie (ale aj najmenej efektívne) je použiť dve vnorené FOR-slučky:

ucelovka = 0;
for i = 1:n
  for j = 1:m
    ucelovka = ucelovka + c(i, j)*x(i, j);
  end
end

Efektívnejší je maticový zápis v tvare

ucelovka = sum(diag(c*x'));

ktorý dá rovnaký výsledok (viete prečo?)

Ostáva pridať ohraničenia. Najskôr nenegativita optimalizovaných premenných:

ohranicenia = [ x(:) >= 0 ];

Tu x(:) znamená vektorizáciu matice a ohraničenie je interpretované po prvkoch. Potom pre každého výrobcu pridáme ohraničenie na jeho maximálnu produkciu:

for i = 1:n
  ohranicenia = ohranicenia + [ sum(x(i, :)) <= p(i) ];
end

A podobne ohraničenia na spotrebu zákazníkov:

for j = 1:m
  ohranicenia = ohranicenia + [ sum(x(:, j)) == s(j) ];
end

Nakoniec problém vyriešime použitím funkcie optimize:

info = optimize(ohranicenia, ucelovka)

pričom je však potrebné zadefinovať konštanty c ($n \times m$ matica cien jednotlivých prepravných trás), vektor p ($n \times 1$ vektor maximalných produkčných kapacít jednotlivých výrobcov) a s ($m \times 1$ vektor požiadaviek jednotlivých zákazníkov).

Ako príklad uvažujme $n=100$ výrobcov a $m=500$ spotrebiteľov s náhodne určenými kapacitami/požiadavkami:

n = 100; % pocet vyrobcov
m = 500; % pocet spotrebitelov
c = rand(n, m)*10;  % nahodne prepravne ceny
p = rand(n, 1)*100; % nahodne vyrobne kapacity
s = rand(m, 1)*10;  % nahodne spotreby

Napriek tomu, že riešime problém s 50000 optimalizovanými premennými, solver GLPK dokáže tento problém vyriešiť za menej ako jednu sekundu:

info = 
    yalmiptime: 0.346750807999999
    solvertime: 0.777837192000000
          info: 'Successfully solved (GLPK-GLPKMEX-CC)'
       problem: 0