ナップザック問題は、動的プログラミングによってO(nW)複雑さで解決できることがわかっています。しかし、これはNP完全問題であると言います。ここで理解するのは難しいと思います。
(nはアイテムの数です。Wは最大ボリュームです。)
O(n*W)
は多項式時間のように見えますが、 not であり、 pseudo-polynomial です。
時間の複雑さは、アルゴリズムが入力の長さのビットの関数としてかかる時間を測定します。ダイナミックプログラミングソリューションは確かにW
の値が線形ですが、W
の長さが指数関数 —それが重要です!
より正確には、knapsack problemの動的解の時間の複雑さは、基本的にネストされたループによって与えられます。
// here goes other stuff we don't care about
for (i = 1 to n)
for (j = 0 to W)
// here goes other stuff
したがって、時間の複雑さは明らかにO(n*W)
です。
アルゴリズムの入力のサイズを線形に増やすとはどういう意味ですか?これは、次第に長いアイテム配列(n
、n+1
、n+2
、...)と次第に長いW
を使用することを意味します(したがって、W
がx
ビット長である場合、1ステップ後にx+1
ビット、次にx+2
ビット、...)。しかし、W
のvalueは、x
とともに指数関数的に増加します。したがって、アルゴリズムは実際には多項式ではなく、指数関数的です(ただし、多項式のようです。
ナップザック0/1問題では、この問題を解決するために2つの入力(1つの配列と1つの整数)が必要です。
N = 10およびW = 8と仮定しましょう:
時間の複雑さT(n) = O(nW) = O(10 * 8)= O(80)
ダブルnのサイズの場合:
n = [n1、n2、n3、...、n1]-> n = [n1、n2、n3、...、n2]
時間の複雑さT(n) = O(nW) = O(20 * 8)= O(160)
しかしWのサイズの2倍の場合、W = 16を意味するわけではありませんが、長さは2倍長くなります。
W = 1000-> W = 2項で10000000 (8ビット長)
so T(n) = O(nW) = O(10 * 128)= O(1280)
必要な時間が指数関数的に増加するため、NPC問題です。
それはすべて、O(...)
内に配置するパラメーターに依存します。
ターゲットの重みが数値W
によって制限されている場合、問題はO(n*W)
複雑度になります(前述)。
しかし、重みが大きすぎて、W
に依存しない複雑さのアルゴリズムが必要な場合、問題はNP完全です。 (O(2^n*n)
ほとんどの素朴な実装で)。
これは、knapsack problemにはpseudo-polynomial解があるため、弱NP-Complete(および強くNP-Completeではありません)。
入力のサイズは、重みの場合はlog(W)
ビットです(「値」および「重み」配列の場合はO(n)
)。
したがって、weightの入力サイズはj = log(W)
(単なるW
ではありません)です。したがって、_W = 2ʲ
_(バイナリが使用されます)。
最終的な複雑さはO(n * W)
です
この
O(n * W)
は、入力サイズの指数関数であるO(n * 2ʲ)
に書き換えることができます。
したがって、この解は多項式ではありません。
この短い説明を読むことができます: ナップザックのNP完全性 。
NP-completeness を理解するには、少し複雑な理論を学ぶ必要があります。ただし、ナップザック問題の効率的なアルゴリズムは [〜#〜] sat [〜#〜] 、 [〜#〜 ] tsp [〜#〜] およびその他。