Pelaksanaan Masalah Knapsack di Jawa

1. Pengenalan

Masalah ransangan adalah masalah pengoptimuman gabungan yang mempunyai banyak aplikasi. Dalam tutorial ini, kita akan menyelesaikan masalah ini di Java.

2. Masalah Knapsack

Dalam masalah ransel, kami mempunyai satu set barang. Setiap item mempunyai nilai berat dan nilai:

Kami mahu memasukkan barang-barang ini ke dalam ransel. Walau bagaimanapun, ia mempunyai had berat:

Oleh itu, kita perlu memilih barang-barang yang berat totalnya tidak melebihi had berat, dan nilai keseluruhannya setinggi mungkin. Sebagai contoh, penyelesaian terbaik untuk contoh di atas adalah memilih item 5kg dan item 6kg, yang memberikan nilai maksimum $ 40 dalam had berat.

Masalah ransel mempunyai beberapa variasi. Dalam tutorial ini, kita akan memberi tumpuan kepada masalah ransel 0-1. Dalam masalah ransel 0-1, setiap item mesti dipilih atau ditinggalkan. Kami tidak boleh mengambil sebahagian daripada item. Kami juga tidak dapat mengambil item berkali-kali.

3. Definisi Matematik

Sekarang mari kita formalkan masalah ransangan 0-1 dalam notasi matematik. Memandangkan satu set item n dan had berat W , kita dapat menentukan masalah pengoptimuman seperti:

Masalah ini sukar NP. Oleh itu, tidak ada algoritma masa polinomial untuk menyelesaikannya pada masa ini. Walau bagaimanapun, terdapat algoritma masa pseudo-polinomial yang menggunakan pengaturcaraan dinamik untuk masalah ini.

4. Penyelesaian berulang

Kita boleh menggunakan formula rekursi untuk menyelesaikan masalah ini:

Dalam formula ini, M (n, w) adalah penyelesaian optimum untuk item n dengan had berat w . Nilai maksimum dua nilai berikut:

  • Penyelesaian optimum dari (n-1) item dengan had berat w (tidak termasuk item - n )
  • Nilai n item -th ditambah penyelesaian optimum dari (n-1) barangan dan w berat tolak daripada n item -th (termasuk n item -th)

Sekiranya berat item ke- n lebih daripada had berat semasa, kami tidak memasukkannya. Oleh itu, ia berada dalam kategori pertama dari dua kes di atas.

Kita dapat menerapkan formula rekursi ini di Jawa:

int knapsackRec(int[] w, int[] v, int n, int W) { if (n  W) { return knapsackRec(w, v, n - 1, W); } else { return Math.max(knapsackRec(w, v, n - 1, W), v[n - 1] + knapsackRec(w, v, n - 1, W - w[n - 1])); } } 

Dalam setiap langkah rekursi, kita perlu menilai dua penyelesaian yang tidak optimum. Oleh itu, masa berjalan penyelesaian rekursif ini adalah O (2n).

5. Penyelesaian Pengaturcaraan Dinamik

Pengaturcaraan dinamik adalah strategi untuk melariskan masalah pengaturcaraan yang sukar. Ideanya adalah untuk menyimpan hasil subproblem sehingga kita tidak perlu mengira semula nanti.

Kami juga dapat menyelesaikan masalah ransel 0-1 dengan pengaturcaraan dinamik. Untuk menggunakan pengaturcaraan dinamik, kita membuat jadual 2 dimensi dengan dimensi dari 0 ke n dan 0 hingga W . Kemudian, kami menggunakan pendekatan bawah-atas untuk mengira penyelesaian optimum dengan jadual ini:

int knapsackDP(int[] w, int[] v, int n, int W) { if (n <= 0 || W <= 0) { return 0; } int[][] m = new int[n + 1][W + 1]; for (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]); } } } return m[n][W]; } 

Dalam penyelesaian ini, kita mempunyai gelung bersarang lebih bilangan item n dan had berat W . Oleh itu, masa berjalan adalah O (nW) .

6. Kesimpulannya

Dalam tutorial ini, kami menunjukkan definisi matematik untuk masalah ransel 0-1. Kemudian kami memberikan penyelesaian rekursif untuk masalah ini dengan pelaksanaan Java. Akhirnya, kami menggunakan pengaturcaraan dinamik untuk menyelesaikan masalah ini.

Seperti biasa, kod sumber untuk artikel tersebut terdapat di GitHub.