Linearno programiranje u Pythonu

Rješenja odabranih problema linearnog programiranja u Pythonu.
lang-cro
python
Published

December 4, 2022

1 Uvjeti

pip install pulp

Import potrebnih stavki iz pulp modula (docs):

from pulp import LpMaximize, LpMinimize, LpProblem, LpStatus, lpSum, LpVariable

2 Zadatak 01

Investitor ulaže u dionice \(D_{1}\) i \(D_{2}\). Očekivani jedinični prinos na dionicu \(D_{1}\) je 0,06 novčanih jedinica, a na dionicu \(D_{2}\) 0,09 novčanih jedinica. Cijena za jednu dionicu \(D_{1}\) je 25 novčanih jedinica, dok je cijena jedne dionice \(D_{2}\) 42 novčane jedinice. Investicijska strategija je sljedeća: investitor ima na raspolaganju 777 novčanih jedinica i to je najviše što želi potrošiti ulaganjem u dionice, te ne želi uložiti u više od 30 komada dionica \(D_{1}\) i \(D_{2}\) zajedno. Formulirajte odgovarajući problem kao model linearnog programiranja, a zatim koristeći se grafičkom metodom, odredite optimalno rješenje.

Formulacija problema je sljedeća:

\[ \begin{aligned} max(0.06 \times D_{1} + 0.09 \times D_{2}) \\ 25 \times D_{1} + 42 \times D_{2} \leq 777 \\ D_{1} + D_{2} \leq 30 \\ D_{1}, D_{2} \geq 0 \\ \end{aligned} \]

Prvo, definirajmo model:

model = LpProblem(name="zadatak_01", sense=LpMaximize)

Nakon toga, potrebno je definirati varijable:

d1 = LpVariable("dionica_01", lowBound=0)
d2 = LpVariable("dionica_02", lowBound=0)

Kroz lowBound=0, automatski smo uključili ograničenje da rješenje u svezi količine dionica mora biti jednako ili veće od nule.

Potom je potrebno definirati objektivnu funkciju modela:

model += lpSum([0.06 * d1, 0.09 * d2])

Investitor je zadao dva ograničenja, a tiču se budžeta i željenih količina dionica:

model += (25 * d1 + 42 * d2 <= 777, "budget")
model += (d1 + d2 <= 30, "quantity")

Naš model u konačnici izgleda ovako:

print(model)
zadatak_01:
MAXIMIZE
0.06*dionica_01 + 0.09*dionica_02 + 0.0
SUBJECT TO
budget: 25 dionica_01 + 42 dionica_02 <= 777

quantity: dionica_01 + dionica_02 <= 30

VARIABLES
dionica_01 Continuous
dionica_02 Continuous

Kako će me za ove zadatke zanimati vrijednost funkcije te optimalna rješenje, olakšati ću si kroz definiranje funkcije print_solution():

def print_solution(model: LpProblem) -> None:
    out = {
        "VrijednostFunkcije": model.objective.value(),
        "VrijednostVarijabli": {f"{var.name}": var.value() for var in model.variables()},
        "VrijednostOgranicenja": {f"{name}": constraint.value() for name, constraint in model.constraints.items()}
    }

    for key, value in out.items():
        if type(value) == dict:
            for _key, _value in value.items():
                print(f"\t\t{key} -> {_key}: {round(_value, 4)}")
        else:
            print(f"{key}: {value}")

Prvo moramo riješiti model:

model.solve() # if 1, model is solved
1
print_solution(model)
VrijednostFunkcije: 1.8476470769999997
        VrijednostVarijabli -> dionica_01: 28.4118
        VrijednostVarijabli -> dionica_02: 1.5882
        VrijednostOgranicenja -> budget: 0.0
        VrijednostOgranicenja -> quantity: 0.0

Dakle, investitor bi trebao kupiti 28.4118 \(D_{1}\) te 1.5882 \(D_{2}\) čime će (ako su zadane pretpostavke točne) ostvariti 1.85 novčanih jedinica.

3 Zadatak 02

Investitor ulaže u dionice \(D_{1}\) i \(D_{2}\). Očekivani jedinični prinos na dionicu \(D_{1}\) je 0,06 novčanih jedinica, a na dionicu \(D_{2}\) 0,09 novčanih jedinica. Cijena za jednu dionicu \(D_{1}\) je 25 novčanih jedinica, dok je cijena jedne dionice \(D_{2}\) 42 novčane jedinice. Investicijska strategija je sljedeća: investitor ima na raspolaganju 777 novčanih jedinica i to je najviše što želi potrošiti ulaganjem u dionice, te ne želi uložiti u više od 28 komada dionica \(D_{1}\) i \(D_{2}\) zajedno. Formulirajte odgovarajući problem kao model linearnog programiranja, a zatim koristeći se grafičkom metodom, odredite optimalno rješenje.

\[ \begin{aligned} max(0.06 \times D_{1} + 0.09 \times D_{2}) \\ 25 \times D_{1} + 42 \times D_{2} \leq 777 \\ D_{1} + D_{2} \leq 28 \\ D_{1}, D_{2} \geq 0 \\ \end{aligned} \]

Dakle, sve je isto, osim što količinsko ograničenje sada iznosi 28.

model = LpProblem(name="zadatak_02", sense=LpMaximize)
d1 = LpVariable("dionica_01", lowBound=0)
d2 = LpVariable("dionica_02", lowBound=0)
model += lpSum([0.06 * d1, 0.09 * d2])
model += (25 * d1 + 42 * d2 <= 777, "budget")
model += (d1 + d2 <= 28, "quantity")            # promjena
model.solve() 
print_solution(model)
VrijednostFunkcije: 1.815882342
        VrijednostVarijabli -> dionica_01: 23.4706
        VrijednostVarijabli -> dionica_02: 4.5294
        VrijednostOgranicenja -> budget: -0.0
        VrijednostOgranicenja -> quantity: -0.0

4 Zadatak 03

Investitor ulaže u dionice \(D_1\) i \(D_2\). Očekivani jedinični prinos na dionicu \(D_1\) je 15 novčanih jedinica, a na dionicu \(D_2\) 11 novčanih jedinica. Cijena za jednu dionicu \(D_1\) je 55 novčanih jedinica, dok je cijena jedne dionice \(D_2\) 43 novčane jedinice. Investicijska strategija je sljedeća: investitor ima na raspolaganju 7575 novčanih jedinica i to je najviše što želi potrošiti ulaganjem u dionice, te ne želi uložiti u više od 45 komada dionica \(D_1\) i \(D_2\) zajedno. Formulirajte odgovarajući problem kao model linearnog programiranja, a zatim koristeći se grafičkom metodom, odredite optimalno rješenje.

Formulacija problema je sljedeća:

\[ \begin{aligned} max(15 \times D_{1} + 11 \times D_{2}) \\ 55 \times D_{1} + 43 \times D_{2} \leq 7575 \\ D_{1} + D_{2} \leq 45 \\ D_{1}, D_{2} \geq 0 \\ \end{aligned} \]

Nešto su drugačije postavke, ali logika je identična:

model = LpProblem(name="zadatak_03", sense=LpMaximize)
d1 = LpVariable("dionica_01", lowBound=0)
d2 = LpVariable("dionica_02", lowBound=0)
model += lpSum([15 * d1, 11 * d2])
model += (55 * d1 + 43 * d2 <= 7575, "budget")
model += (d1 + d2 <= 45, "quantity")
model.solve() 
print_solution(model)
VrijednostFunkcije: 675.0
        VrijednostVarijabli -> dionica_01: 45.0
        VrijednostVarijabli -> dionica_02: 0.0
        VrijednostOgranicenja -> budget: -5100.0
        VrijednostOgranicenja -> quantity: 0.0

Knjiga kao rješenje navodi:

\[ \begin{aligned} M(31.25, 18.75), \\ z^{*} = 675 \end{aligned} \]

\(\ldots\) što je očito pogrešno, jer \(D_{1} + D_{2} = 31.25 + 18.75 = 50\), dok se kao uvjet navodi \(D_{1} + D_{2} \leq 45\).

5 Zadatak 04

Neki investitor će na kraju godine raspolagati s 4000 novčanih jedinica koje želi investirati u dionice \(D_1\) i \(D_2\). Godišnji prinos na dionicu \(D_1\) iznosi 6%, a na dionicu \(D_2\) 7.5%. Faktor rizika za dionicu \(D_1\) iznosi 0,01, a za dionicu \(D_2\) 0,015. Odlučio je investirati najmanje 115 novčanih jedinica u dionicu \(D_1\) i najmanje 188 novčanih jedinica u dionicu \(D_2\). Pretpostavljajući da će zadani plan investiranja donijeti najmanje 7% od ukupne vrijednosti investicije, potrebno je minimizirati ukupni rizik investiranja. Koristeći se grafičkom metodom, odredite optimalno rješenje (ako postoji) kao problem linearnog programiranja, a zatim ga interpretirajte.

Formulacija problema je sljedeća:

\[ \begin{aligned} min(0.01 \times D_{1} + 0.015 \times D_{2}) \\ D_{1} + D_{2} \leq 4000 \\ D_{1} \geq 115 \\ D_{2} \geq 188 \\ 0.06 \times D_{1} + 0.075 \times D_{2} \geq 280 \\ D_{1}, D_{2} \geq 0 \end{aligned} \]

Dakle:

model = LpProblem(name="zadatak_03", sense=LpMinimize)

# vars
d1 = LpVariable("dionica_01", lowBound=0)
d2 = LpVariable("dionica_02", lowBound=0)

# obj func
model += lpSum([0.01 * d1, 0.015 * d2])

# constraints
model += (d1 + d2 <= 4000, "total_budget")
model += (d1 >= 115, "preference_d1")
model += (d2 >= 188, "preference_d2")
model += (0.06 * d1 + 0.075 * d2 >= 280, "investment_goal") # 280 = 4000 * 0.07

model.solve()

print_solution(model)
VrijednostFunkcije: 53.333333499999995
        VrijednostVarijabli -> dionica_01: 1333.3333
        VrijednostVarijabli -> dionica_02: 2666.6667
        VrijednostOgranicenja -> total_budget: 0.0
        VrijednostOgranicenja -> preference_d1: 1218.3333
        VrijednostOgranicenja -> preference_d2: 2478.6667
        VrijednostOgranicenja -> investment_goal: 0.0

Prema knjizi, rješenje za ovaj zadatak je:

\[ \begin{aligned} M(4665.42, 188), \\ z^{*} = 49.4742 \end{aligned} \]

\(\ldots\) što također smatram pogrešnim.

Naime:

sol_d1 = 4665.52
sol_d2 = 188

z = 0.01 * sol_d1 + 0.015 * sol_d2
print("z (vrijednost funkcije):", round(z, 5))
z (vrijednost funkcije): 49.4752

Dakle, vrijednost funkcije je točno izračunata (zanemarimo očiti tipfeler). No, nije zadovoljeno ograničenje d1 + d2 <= 4000, jer sam \(D_{1}\) prelazi vrijednost od 4000.

Dostupna rješenja moguće je i grafički prikazati:

Dakle, iz prikazanog grafičkog rješenja vidljivo je da je autor zadovoljio samo dva ograničenja.

6 Reference

  1. Gelo, T. (ur.) (2018) Odabrana poglavlja matematičkih metoda za upravljanje financijskom imovinom. Zagreb. Ekonomski fakultet, str. 25.