Hátizsákos probléma megvalósítása Java-ban

1. Bemutatkozás

A hátizsák probléma egy kombinatorikus optimalizálási probléma, amely sok alkalmazással rendelkezik. Ebben az oktatóanyagban megoldjuk ezt a problémát Java-ban.

2. A hátizsák probléma

A hátizsákproblémában van egy sor elemünk. Minden elemnek van súlya és értéke:

Ezeket a tárgyakat hátizsákba akarjuk tenni. Van azonban súlykorlátja:

Ezért olyan elemeket kell választanunk, amelyek össztömege nem haladja meg a súlyhatárt, és összértékük a lehető legmagasabb. Például a fenti példa legjobb megoldása az 5 kg-os és a 6 kg-os elem kiválasztása, amelyek maximális értéke 40 USD lehet a súlyhatáron belül.

A hátizsák problémának több változata van. Ebben az oktatóanyagban a 0-1 hátizsák problémára fogunk összpontosítani. A 0-1 hátizsákproblémában mindegyik elemet ki kell választani, vagy hátrahagyni. Részes mennyiséget nem vehetünk fel egy tételből. Emellett nem vehetünk fel többször egy elemet.

3. Matematikai meghatározás

Formalizáljuk most a 0-1 hátizsák problémát matematikai jelöléssel. Adott egy sor n tételek és a súlyhatár W, meghatározhatjuk az optimalizálási problémát:

Ez a probléma NP-nehéz. Ezért jelenleg nincs polinomiális idő algoritmus annak megoldására. Van azonban egy álpolinomiális idő algoritmus, amely dinamikus programozást használ erre a problémára.

4. Rekurzív megoldás

Rekurziós képletet használhatunk a probléma megoldására:

Ebben a képletben M (n, w) az optimális megoldás a n súlykorlátozású tételek w. Ez a következő két érték maximuma:

  • Az optimális megoldás (n-1) súlykorlátozású tételek w (kivéve a n-edik tétel)
  • A. Értéke n-adik tétel plusz az optimális megoldás innen (n-1) tételek és w mínusz súlya n-adik tétel (beleértve a n-edik tétel)

Ha a súlya n-th elem meghaladja az aktuális súlyhatárt, nem vesszük bele. Ezért a fenti két eset első kategóriájába tartozik.

Meg tudjuk valósítani ezt a rekurziós képletet Java-ban:

int hátizsákRec (int [] w, int [] v, int n, int W) {if (n W) {visszatérési hátizsákRec (w, v, n - 1, W); } else {return Math.max (hátizsákRec (w, v, n - 1, W), v [n - 1] + hátizsákRec (w, v, n - 1, W - w [n - 1]); }} 

Minden rekurziós lépésben két optimálisnál alacsonyabb megoldást kell értékelnünk. Ezért ennek a rekurzív megoldásnak a futási ideje: O (2n).

5. Dinamikus programozási megoldás

A dinamikus programozás stratégia az egyébként exponenciálisan nehéz programozási problémák linearizálására. Az ötlet az, hogy az alproblémák eredményeit tároljuk, hogy később ne kelljen újra kiszámítanunk őket.

A 0-1 hátizsák problémát dinamikus programozással is megoldhatjuk. A dinamikus programozás használatához először létrehozunk egy 2-dimenziós táblázatot, amelynek méretei 0 és n és 0-ig W. Ezután alulról felfelé irányuló megközelítést alkalmazunk az optimális megoldás kiszámításához ezzel a táblázattal:

int hátizsákDP (int [] w, int [] v, int n, int W) {if (n <= 0 || W <= 0) {visszatér 0; } int [] [] m = új int [n + 1] [W + 1]; mert (int j = 0; j <= W; j ++) {m [0] [j] = 0; } for (int i = 1; i <= n; i ++) {for (int j = 1; j j) {m [i] [j] = m [i - 1] [j]; } else {m [i] [j] = Math.max (m [i - 1] [j], m [i - 1] [j - w [i - 1]] + v [i - 1]); }}} visszatér m [n] [W]; } 

Ebben a megoldásban beágyazott hurok van az elemszám felett n és a súlyhatár W. Ezért ez a futási idő O (nW).

6. Következtetés

Ebben az oktatóanyagban megmutattuk a 0-1 hátizsák probléma matematikai definícióját. Ezután rekurzív megoldást adtunk erre a problémára a Java megvalósításával. Végül dinamikus programozást használtunk a probléma megoldására.

Mint mindig, a cikk forráskódja elérhető a GitHubon.