整数Mとターゲット合計kのセットがあります。一緒に追加されたときに、先に進むことなくkに最も近いMのサブセットを見つけたいと思います。
例えば:
M = {1, 3, 5, 5, 14}
k = 12
answer = {1, 5, 5}
because 1 + 5 + 5 = 11 and there is no way to make 12.
サブセットには最大4つの要素を含めることができるという追加の制約があります。
私のアプリケーションでは、| M |のサイズ大きくなる可能性があります(数千要素程度)。妥当な時間内に最適な答えを見つけることができない場合、私は少なくとも「良い」答えを与える解決策に興味があります。
現在、私はこの問題を10,000個のランダムなサブセットを生成し、最も近いものを選択することで解決しています。これが実際にどれほど最適であるかはわかりませんが、それに関する洞察は私にとっても興味深いでしょう。
選択できるアイテムの数には制限があるため、かなり簡単なアルゴリズムでそれを行うことができます。
アルゴリズムは、「世代」で可能な合計を生成します。世代の各要素は、合計を表す数値と、その合計を作成するために使用されたM
のインデックスのNタプルで構成されます。
世代ゼロは空です。世代X+1
は、世代X
をウォークし、M
の要素をその世代の各値に追加し、次の世代の合計を記録することによって生成されますX+1
。
合計を計算する前に、Nタプルをチェックして、追加する数値のインデックスが存在するかどうかを確認します。ある場合は、番号をスキップしてください。次に、合計を確認します。X+1
の合計の中に既に存在する場合は、無視してください。それ以外の場合は、新しい合計とインデックスの新しいNタプルを記録します(世代X
からNタプルに追加した数のインデックスを追加します)。
これが入力に対してどのように機能するかを次に示します。
ジェネレーション0:空
第1世代:
1 - {0}
3 - {1}
5 - {2}
14 - {4}
第2世代:
4 - {0, 1}
6 - {0, 2}
8 - {1, 2}
10 - {2, 3}
15 - {0, 4}
17 - {1, 4}
19 - {2, 4}
ジェネレーション3:
9 - {0, 1, 2}
11 - {0, 2, 3}
13 - {1, 2, 3}
18 - {0, 1, 4}
20 - {0, 2, 4}
22 - {1, 2, 4}
24 - {2, 3, 4}
第4世代:
14 - {0, 1, 2, 3}
23 - {0, 1, 2, 4}
25 - {0, 2, 3, 4}
27 - {1, 2, 3, 4}
これで、4つの世代を検索して、ターゲット番号k
に最も近い番号を探すことができます。
ターゲットの合計kが大きすぎない場合は、 http://en.wikipedia.org/wiki/Subset_sum_problem#Pseudo-polynomial_time_dynamic_programming_solution を参照してください-これを使用して、どの番号を示すビットマップを作成できますサブセットを使用して作成できます。次に、ビットマップでkに最も近い可能な数を選択します。
問題を4つの部分に分割します。
正確に1つの要素を含む合計
単純にループして、ターゲット以下の最大値を見つけます。
正確に2つの要素を含む合計
ダブルforループを使用して、ターゲットより大きくない最大の合計を見つけます。
正確に3つの要素を含む合計( SUM と同様)
要素を並べ替える
二重forループを使用して、ターゲットから2つの値を差し引いたバイナリ検索を実行し、小さい値を探して、ターゲットより大きくない最大の合計を見つけます。
正確に4つの要素を含む合計
要素を並べ替えます(既に完了しています)
2つのforループを使用して、2つの要素のすべての合計を生成します。
次に、そのような合計ごとに、ターゲットの合計に対してバイナリ検索を実行し、この合計を構成する値を含まない値が見つかるまで、より小さい値を探します。
同様の問題(正確な合計)に対してこのアプローチを使用するコードについては this を参照してください。
ケースの平均実行時間(?)= O(n + n^2 + n^2 log n + n^2 log n) = O(n^2 log n)
。
最後の問題の実行時間を決定するのはやや難しいですが、最悪の場合はO(n^4 log n)
と同じくらい悪いかもしれません。まれに発生し、同じ実行内で一部の時間を短縮する必要があるため、全体の実行時間は短縮される可能性があります。