Závěrečná zpráva 3

Jakub Holý: Řešení problému batohu
 dynamickým programováním, metodou větví a hranic a heuristikou
36PAA LS 2003

Úvod

Teoretický úvod s dovolením vynechám, neboť jsou obsaženy v zadání (pro zvědavé viz odkazy).

Popis algoritmů

Stručně popíši implementaci užitých algoritmů, pro podrobnější vysvětlení viz komentáře ve zdrojových souborech (hlavně před metodou solve).

Dynamické programování (Dynamic Programming)

Jádrem tohoto řešení je rozložení problému na menší s využitím faktu, že je-li řešení problému s k věcmi a nosností N optimální a obsahuje k-tou věc, pak optimální řešení problému s prvními k-1 věcmi a nosností (N - váha k) je toto řešení po vyjmutí k-té věci. Výhoda DP spočívá v tom, že si pamatujeme co jsme již počítali a nepočítáme to znovu. K tomu mi slouží pole bestcosts[w], w = 0 ... nosnost, které v k-tém průchodu hlavní smyčkou obsahuje nejlepší možné řešení pro prvých k věcí a nosnost batohu rovnou w. Zde je tedy srdce algoritmu:

for (int i = 0; i < nr_items; i++){

   for (int w = max_load; w > 0; --w){
     if (items[i]->weight < w) {
    // bestcosts[w] = MAXIMUM of
    // 1. best solution for k < i first items and capacity w, without i-th item, 
    // 2. best solution for k = i first items and capacity w, including i-th item
    // (which is based on the best solution for k < i and capacity (w - i-th item's weight))
    bestcosts[w] = max( bestcosts[w], bestcosts[w - items[i]->weight] + items[i]->cost ); 
    }// if
  }// for all weights

}// for all items
solution_cost = bestcosts[max_load];

Metoda větví a hranic (Branch and Bound)

Algoritmus je založen na řešení hrubou silou z první úlohy. Rozdíl je v tom, že některé větve nejsou prozkoumávány, resp. metoda descend  skončí už když se dostane do situace, že rozvíjená větev nemůže být lepší než dosud dosažené řešení (a neschází ji celou), a metoda mount obdobně pomíjí neperspektivní řešení. Jádro změn leží ve funkcích (viz dynamic_progr.hh.html) do_notExclude(int item) pro mount a lessThanBest(int item, int preceding_cost) pro descend. Obě staví na porovnání ceny nejlepšího dosud dosaženého řešení a horní meze, čili největší potenciální ceně rozvíjeného řešení, která se - při zvažování i-té věci - spočte jako součet cen věcí < i které v řešení jsou a součet cen všech věcí > i [případně + cena i-té věci].
Očividně takto určená horní mez někdy značně nadhodnocuje možnosti rozvíjeného řešení. To je cena za to, že jsem dal přednost jednoduchosti před kvalitou. Podstatně lepší by bylo užít Kolesarův algoritmus, ale s tím jsem se bohužel seznámil příliš pozdě.

Heuristika "cena/váha nebo pouze nejcennější"

Tento algoritmus je téměř totožný s heuristikou z první úlohy, jenom bylo přidáno porovnání s řešením obsahujícím pouze nejcennější věc a vybrání toho lepšího.

Výsledky

Značení: DP = Dynamické Programování, BB = metoda řezů a větví, HR = Heuristika

 1. Cena řešení

Cena řešení 
Věcí DP BB HR Optimální
4 352 352 347 352
10 1125 1125 1110 1125
15 1814 1814 1808 1814
20 2335 2335 2321 2335
22 2501 2501 2488 2501
25 2954 2954 2941 2954
27 3143 3143 3122 3143
30 3581 3581 3563 3581
32 3694 3694 3678 3694
35 4081 4081 4067 4081
37 4222 4222 4205 4222
40 4628 4628 4612 4628

Dynamické programování a metoda větví a řezů vždy (nejen v tomto případě) najdou optimální řešení. Heuristika dává i neoptimální řešení, ale v drtivé většině případů se rozdíl od optima drží pod 1% a nikdy nepřekročí 2% (viz úkol 1 pro [horší verzi heuristiky]). Přestože naměřené výsledky mají jistou vypovídací hodnotu, nelze je zobecňovat. Připomeňme jenom, že tato heuristika má teoreticky prokázanou úspěšnost  pouze > 50% (tzn. je zaručeno, že dosáhne přinejmenším poloviny ceny optima).

2. Počet kroků k nalezení řešení

Počet kroků 
Věcí DP BB HR
4 400 12 4
10 1000 146 10
15 3000 178 15
20 5000 3491 20
22 5500 12755 22
25 7500 33225 25
27 9450 38631 27
30 12000 184291 30
32 14400 263077 32
35 17500 677279 35
37 20350 721623 37
40 24000 8576258 40

Z hlediska počtu kroků je zdaleka nejúspěšnější heuristika, vzhledem k tomu, že počet kroků je roven počtu věcí. Docela rozumné je ještě dynamické programování s O(počet věcí*nosnost). Zato metoda řezů a větví roste opravdu značně rychle, i když zpočátku je lepší než dynamické programování; kdyby používala lepší heuristiku, byly by výsledky přijatelnější, ovšem na asymptotické složitosti by se nejspíš příliš nezměnilo.

3. Doba výpočtu řešení

Následující údaje jsou pouze orientační, vzhledem k tomu, že čas běhu programu závisí na kvalitě překladače a rychlosti počítače. Slouží k ilustraci problému růstu počtu kroků.

Doba výpočtu 
Věcí DP BB HR
4 28 18 19
10 48 17 27
15 114 32 31
20 181 70 36
22 194 168 38
25 260 432 40
27 316 505 44
30 399 2061 40
32 475 2946 48
35 567 7653 52
37 661 7672 55
40 770 93762 59

Závěr

Z dosud řečeného plyne, že jako vítěz se pro naše vstupní data a implementaci jeví heuristika. Při praktické aplikaci je však zapotřebí pečlivě zvážit, zda je důležitější cena řešení, rychlost (největší pro heuristiku) nebo něco mezi, a to s ohledem na vnější podmínky jako dostupné výpočetní vybavení, čas a charakteristika instance.

Přílohy

Zdrojové kódy programového řešení:
Soubor Popis
common.hh.html rodičovská třída všech algoritmů
dynamic_progr.hh.html
dynamic_progr.cc.html
dynamické programování
branch_bound.hh.html
branch_bound.cc.html
metoda větví a hranic
heuristic2.hh.html
heuristic2.cc.html
heuristika
ksack_in.hh.html
ksack_in.cc.html
zpracování vstupního souboru dat
main.cc.html hlavní program, rozhraní mezi uživatelem a algoritmem