====== Úkol 3 - problém batohu (knapsack problem) ====== * CTU Prague, MI-PAA, ZS 2011 * Autor: **Tomáš Borovička (borovto1)** **Zadání** * Naprogramujte řešení problému batohu: - metodou větví a hranic (B&B) tak, aby omezujícím faktorem byla hodnota optimalizačního kritéria. Tj. použijte ořezávání shora (překročení kapacity batohu) i zdola (stávající řešení nemůže být lepší než nejlepší dosud nalezené) - metodou dynamického programování (dekompozice podle vah nebo podle cen) - modifikujte tento program tak, aby pracoval s omezenou přesností zobrazení vah nebo cen (podle zvoleného přístupu k dynamickému programování) - aproximativní algoritmus. ===== Popis problému ===== Je dán batoh s kapacitou W a monožina předmětů N, kde každý předmět má svou váhu w_i a cenu v_i. Úkolem je najít takovou podmonožinu N, která maximalizuje cenu předmětů v batohu a nepřesáhne svou vahou kapacitu batohu W. Problém batohu je kombinatorický optimalizační problém spadající do třídy NP úplných problémů. ===== Řešení ===== ==== Řešení metodou větví a hranic (branch & bound) ==== * Metodaje vychází z původního řešení hrubou silou. Principem metody větví a hranic je ořezávání stavového prostoru o stavy, které nevedou k optimálnímu řešení. * **Ořezávání shora**: stav je ukončen, pokud by měla být překročena kapacita batohu. * **Ořezávání zdola**: stav je ukončen, pokud je aktuální řešení horššší než nejlepší nalezené. === Část zdrojového kódu - řešení metodou větví a hranic === private Solution SolveBranchAndBound(Solution solution, int idx) { if (idx < 0) return solution; // branch and bound if (_residualValues[idx] + solution.Value <= _bestSolution.Value) return solution; if (solution.ResCapacity < solution.Instance.Items[idx].Weight) return SolveBranchAndBound(solution, idx - 1); var s1 = solution; var s2 = solution.Clone(); // s2.Bits[idx] = true; s2.Value += solution.Instance.Items[idx].Value; s2.ResCapacity -= solution.Instance.Items[idx].Weight; s1 = SolveBranchAndBound(s1, idx - 1); s2 = SolveBranchAndBound(s2, idx - 1); solution = s1.Value > s2.Value ? s1 : s2; _bestSolution = solution; return solution; } ==== Řešení dynamickým programováním ==== * Dynamické programování je založeno na dekompozici problémů, řešení jednotlivých subproblému a následné skládání problému s využitím těchto vyřešených subproblémů. Následující algoritmus reší problém dynamickým programováním s dekompozicí podle kapacity. Pro ukládání vesledků využívá algoritmus dvourozměrného pole _solutionCache[kapacita+1, pocet prvku]. Při průchodu pak algoritmus testuje, zda není subproblém vyřešen a pokud ano využije vypočtených hodnot. === Část zdrojového kódu - řešení dynamickým programováním === private Solution SolveDynamic(Solution solution, int idx, int weight) { if (weight < 0) return new Solution(solution.Instance){Value = int.MinValue}; if (idx < 0) return new Solution(solution.Instance); if (_solutionCache[weight, idx] != null) { return _solutionCache[weight, idx].Clone(); } var s1 = solution; var s2 = solution.Clone(); s1 = SolveDynamic(s1, idx - 1,weight); s2 = SolveDynamic(s2, idx - 1,weight-solution.Instance.Items[idx].Weight); s2.Value += solution.Instance.Items[idx].Value; s2.ResCapacity -= solution.Instance.Items[idx].Weight; var result = s1.Value > s2.Value ? s1 : s2; _solutionCache[weight, idx] = result.Clone(); return result; } ==== FPTAS algoritmus ==== * FTPAS algoritmus je plně polynomiální časově aproximační algoritmus. Respektive jedná se stále o stejný dynamický algoritmus ovšem využívající plně polynomiální časově aproximační schéma (FPTAS). To umožňuje řešit problém s libovolně stanovenou přesností , kde výpočetní složitost algoritmu se zvyšováním přesnosti neporoste hůře, než polynomiálně. * Princip algoritmu spočívá v "zaokrouhlení" parametru jež má vliv na počet stavů, které se mohou při řešení vyskytovat. V následujícím algoritmu je tímto parametrem váha předmětů (a s nimi samozřejmě kapacita batohu). Samotné zaokrouhlení spočívá v zanedbání posledních x bitů z váhy předmětu (neboli ve vydělení váhy mocninami 2). === Část zdrojového kódu - FPTAS algoritmus === public void RoundByWeight(int bitsToRound) { Capacity = Capacity >> bitsToRound; foreach (var item in Items) { item.Weight = item.Weight >> bitsToRound; } } public Solution SolveFTPAS(Instance instance) { instance.RoundByWeight(BitsToRound); _solutionCache = new Solution[instance.Capacity + 1, instance.Items.Count]; _solutionCache.Initialize(); return SolveDynamic(new Solution(instance), instance.Count - 1, instance.Capacity); } ===== Výsledky měření ===== ==== Platforma a HW konfigurace ==== Windows Server 2008 EE x64, Intel Core2 Duo E8500@3.16GHz, 8 GB DDR2 ==== Čas výpočtu ==== Z tabulky a grafu 1 (v logaritmickém měřítku) je vidět exponenciálně rostoucí čas řešení hrubou silou i metodou branch and bound, i když metodou B&B roste čas výrazně pomaleji. Naopak téměř lineární růst výpočetní času s velikostí instance můžeme pozorovat při dynamickém programování a aproximativním řešení. ^N ^BF (ms)^H (ms)^B&B (ms)^DP (ms)^FPTAS(2) (ms)^ |4 |9.67E-04 |7.80E-04| 1.30E-01 |1.10E-02 |3.75E-03| |10 |4.90E-02 |1.97E-03| 1.59E-02 |4.27E-02 |1.47E-02| |15 |2.36E+00 |2.90E-03| 5.37E-02 |1.78E-01 |4.87E-02| |20 |6.74E+01 |4.05E-03| 1.07E+00 |3.79E-01 |1.02E-01| |22 |2.54E+02 |4.42E-03| 6.43E+00 |4.43E-01 |1.10E-01| |25 |2.14E+03 |4.90E-03| 2.85E+01 |7.49E-01 |6.24E-02| |27 |9.49E+03 |5.36E-03| 7.74E+01 |8.57E-01 |9.48E-02| |30 |7.40E+04 |5.74E-03| 4.17E+02 |1.45E+00 |2.51E-01| |32 |3.02E+05 |6.40E-03| 1.25E+03 |2.03E+00 |2.50E-01| |35 |2.42E+06 |7.18E-03| 8.67E+03 |2.50E+00 |3.12E-01| |37 |9.98E+06 |7.02E-03| 2.30E+04 |2.23E+00 |6.30E-01| | {{:school:fit:mipaa:zavislostcasuvypostunapostupredmetu2.png|}} | | Graf 1 | ==== Závislost chyby a výpočetního času aproximativního algoritmu na zvolené přesnosti zobrazení ==== === Závislost chyby na zvolené přesnosti zobrazení === * Podle očekávání relativní chyba roste s počtem zanedbaných bitů jak ukazuje Graf 2. Výraznější chyba začíná být od tří zaokrouhlených bitů, kde u většiny instancí překračuje hranici 10%. ^ ^ 4^ 10^ 15^ 20^ 22^ 25^ 27^ 30^ 32^ 35^ 37^ ^FPTAS 0| 0.00%| 0.00%| 0.00%| 0.00%| 0.00%| 0.00%| 0.00%| 0.00%| 0.00%| 0.00%| 0.00%| ^FPTAS 1| 0.78%| 1.20%| 0.65%| 1.09%| 1.05%| 1.21%| 0.95%| 1.09%| 0.89%| 0.97%| 0.87%| ^FPTAS 2| 5.33%| 3.91%| 1.55%| 2.64%| 3.02%| 3.17%| 2.78%| 3.03%| 2.59%| 2.63%| 2.32%| ^FPTAS 3| 6.09%| 6.60%| 2.65%| 5.46%| 6.82%| 6.31%| 5.46%| 6.15%| 5.80%| 5.53%| 4.90%| ^FPTAS 4| 18.00%| 13.82%| 5.10%| 9.69%| 12.20%| 11.39%| 10.11%| 11.33%| 10.79%| 10.85%| 9.90%| ^FPTAS 5| 28.89%| 21.04%| 5.22%| 10.28%| 13.44%| 11.90%| 10.63%| 11.47%| 11.04%| 10.99%| 10.05%| ^FPTAS 6| 32.70%| 22.75%| 5.22%| 10.28%| 13.48%| 11.90%| 10.63%| 11.47%| 11.04%| 10.99%| 10.05%| |{{:school:fit:mipaa:fptas_relativnichyba.png|}}| | Graf 2 | === Závislost výpočetního času na zvolené přesnosti zobrazení === * Zanedbáním x nejméně významných bytů redukujeme počet možných stavů problému, důsledkem čehož musí klesat i výpočetní čas. Důkazem je Graf 3, kde FTPAS 0 je vlastně dynamický algoritmus a FTPAS 6 je řešení se zanedbáním 6 posledních bitů. ^ ^ 4^ 10^ 15^ 20^ 22^ 25^ 27^ 30^ 32^ 35^ 37^ ^FPTAS 0| 0.001872| 0.043992| 0.181897| 0.391563| 0.468315| 0.904806| 0.717605| 1.248008| 2.184014| 2.184014| 2.808018| ^FPTAS 1| 0.001872| 0.025272| 0.092665| 0.188761| 0.211537| 0.280802| 0.343202| 0.624004| 2.184014| 0.936006| 2.496016| ^FPTAS 2| 0.002184| 0.013416| 0.048048| 0.094225| 0.111697| 0.124801| 0.124801| 0.343202| 0.624004| 0.624004| 1.248008| ^FPTAS 3| 0.000936| 0.008424| 0.020592| 0.047112| 0.050544| 0.0312| 0.0624| 0.124801| 0.183145| 0.312002| 0.312002| ^FPTAS 4| 0.001248| 0.005616| 0.00936| 0.017784| 0.024024| 0.0624| 0.044616| 0.0312| 0.078001| 0.083617| 0.112633| ^FPTAS 5| 0.001248| 0.002496| 0.003744| 0.007176| 0.009672| 0.01248| 0.01716| 0.021528| 0.026832| 0.029016| 0.037128| ^FPTAS 6| 0.000936| 0.00156| 0.002496| 0.002184| 0.00312| 0.00312| 0.004992| 0.006552| 0.004992| 0.00468| 0.005304| |{{:school:fit:mipaa:fptas_zavislostcasuvypostunapostupredmetu.png|}}| | Graf 3 | ===== Závěr ===== Jako nejlepší řešení problému batohu se ukázalo řešení dynamickým programováním, pokud bychom si mohli dovolit ztrátu přesnosti, může nám výrazně snížit výpočetní čas aproximativní řešení s využitím FPTAS. ===== Source code ===== ===== References ===== * [[http://en.wikipedia.org/wiki/Knapsack_problem]] * [[http://rosettacode.org/wiki/Knapsack_problem/0-1]]