[解決済み】与えられた和になるように数字の組み合わせの可能性を探す
質問
与えられたセットから追加される可能性のあるすべての組み合わせをテストするにはどうしたらよいでしょうか?
N
を足すと、ある最終的な数字になりますか?
簡単な例です。
-
加算する数値のセット。
N = {1,5,22,15,0,...}
-
希望する結果
12345
解決方法は?
この問題は、すべての可能な和を再帰的に組み合わせて、目標に達する和をフィルタリングすることで解決することができる。以下はPythonによるアルゴリズムである。
def subset_sum(numbers, target, partial=[]):
s = sum(partial)
# check if the partial sum is equals to target
if s == target:
print "sum(%s)=%s" % (partial, target)
if s >= target:
return # if we reach the number why bother to continue
for i in range(len(numbers)):
n = numbers[i]
remaining = numbers[i+1:]
subset_sum(remaining, target, partial + [n])
if __name__ == "__main__":
subset_sum([3,9,8,4,5,7,10],15)
#Outputs:
#sum([3, 8, 4])=15
#sum([3, 5, 7])=15
#sum([8, 7])=15
#sum([5, 10])=15
この種のアルゴリズムは、次のように非常によく説明されています。 スタンフォード大学の抽象プログラミングの講義 - このビデオは、再帰がどのように解の並べ替えを生成するのかを理解するのに非常にお薦めです。
編集
上記をジェネレータ関数として、もう少し便利にしてみました。Python 3.3+ が必要なのは
yield from
.
def subset_sum(numbers, target, partial=[], partial_sum=0):
if partial_sum == target:
yield partial
if partial_sum >= target:
return
for i, n in enumerate(numbers):
remaining = numbers[i + 1:]
yield from subset_sum(remaining, target, partial + [n], partial_sum + n)
以下は、同じアルゴリズムのJava版です。
package tmp;
import java.util.ArrayList;
import java.util.Arrays;
class SumSet {
static void sum_up_recursive(ArrayList<Integer> numbers, int target, ArrayList<Integer> partial) {
int s = 0;
for (int x: partial) s += x;
if (s == target)
System.out.println("sum("+Arrays.toString(partial.toArray())+")="+target);
if (s >= target)
return;
for(int i=0;i<numbers.size();i++) {
ArrayList<Integer> remaining = new ArrayList<Integer>();
int n = numbers.get(i);
for (int j=i+1; j<numbers.size();j++) remaining.add(numbers.get(j));
ArrayList<Integer> partial_rec = new ArrayList<Integer>(partial);
partial_rec.add(n);
sum_up_recursive(remaining,target,partial_rec);
}
}
static void sum_up(ArrayList<Integer> numbers, int target) {
sum_up_recursive(numbers,target,new ArrayList<Integer>());
}
public static void main(String args[]) {
Integer[] numbers = {3,9,8,4,5,7,10};
int target = 15;
sum_up(new ArrayList<Integer>(Arrays.asList(numbers)),target);
}
}
全く同じヒューリスティックです。私のJavaは少し錆びついていますが、理解しやすいと思います。
JavaソリューションのC#変換。 (@JeremyThompson) さんによるものです。
public static void Main(string[] args)
{
List<int> numbers = new List<int>() { 3, 9, 8, 4, 5, 7, 10 };
int target = 15;
sum_up(numbers, target);
}
private static void sum_up(List<int> numbers, int target)
{
sum_up_recursive(numbers, target, new List<int>());
}
private static void sum_up_recursive(List<int> numbers, int target, List<int> partial)
{
int s = 0;
foreach (int x in partial) s += x;
if (s == target)
Console.WriteLine("sum(" + string.Join(",", partial.ToArray()) + ")=" + target);
if (s >= target)
return;
for (int i = 0; i < numbers.Count; i++)
{
List<int> remaining = new List<int>();
int n = numbers[i];
for (int j = i + 1; j < numbers.Count; j++) remaining.Add(numbers[j]);
List<int> partial_rec = new List<int>(partial);
partial_rec.Add(n);
sum_up_recursive(remaining, target, partial_rec);
}
}
Rubyのソリューションです。 (@emailleninさん)
def subset_sum(numbers, target, partial=[])
s = partial.inject 0, :+
# check if the partial sum is equals to target
puts "sum(#{partial})=#{target}" if s == target
return if s >= target # if we reach the number why bother to continue
(0..(numbers.length - 1)).each do |i|
n = numbers[i]
remaining = numbers.drop(i+1)
subset_sum(remaining, target, partial + [n])
end
end
subset_sum([3,9,8,4,5,7,10],15)
編集:複雑性の議論
他の方もおっしゃっていますが、これは NP困難問題 . これは指数時間O(2^n)で解くことができ、例えばn=10の場合、1024通りの解が存在することになります。もし、到達しようとする目標が低い範囲であれば、このアルゴリズムはうまくいきます。つまり、例えば
subset_sum([1,2,3,4,5,6,7,8,9,10],100000)
は 1024 本の枝を生成します。これは、ターゲットが可能な解決策をフィルタリングすることがないためです。
一方
subset_sum([1,2,3,4,5,6,7,8,9,10],10)
は 175 本の枝しか生成しません。
10
は、多くの組み合わせをフィルタリングするようになります。
もし
N
と
Target
が大きな数字である場合は、近似解に移行する必要があります。
関連
-
[解決済み] どちらが大きいですか?O(log*n)とO(loglog n)
-
[解決済み] 深さ優先グラフアルゴリズムの時間複雑性【非公開
-
[解決済み] 簡単な面接問題が難しくなった:1~100の数字が与えられたとき、ちょうどk個の数字が欠けていることを見つけなさい。
-
[解決済み] 40 億の整数以外の整数を生成する。
-
[解決済み] nからk個の要素の組み合わせをすべて返すアルゴリズム
-
[解決済み] リストの要素のすべての可能な組み合わせを得るには?
-
[解決済み】画像処理。コカ・コーラ缶」認識のためのアルゴリズム改良
-
[解決済み】ssl証明書はどのように検証されるのですか?
-
[解決済み】iTunes 11の曲リストに色をつけるアルゴリズムはどうなっているのでしょうか?[クローズド]
-
[解決済み】異なるサイズの長方形を、かなり最適な方法で可能な限り小さな長方形に詰め込むには、どのようなアルゴリズムが使用できるだろうか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] Breadth First Searchの時間複雑性解析
-
[解決済み] 最大スパニングツリーの求め方は?
-
[解決済み] 迷路の生成に適したアルゴリズムとは?[クローズド]
-
[解決済み] 深さ優先グラフアルゴリズムの時間複雑性【非公開
-
[解決済み】有向グラフのサイクルを検出する最適なアルゴリズム【クローズド
-
[解決済み】遺伝的アルゴリズム/遺伝的プログラミングの良い解決例とは?[クローズド]
-
[解決済み】ポリゴンの膨張・収縮(オフセット、バッファリング)のためのアルゴリズム
-
[解決済み】純粋な関数型プログラミングの効率性
-
[解決済み】セグメントツリー、インターバルツリー、バイナリーインデックスツリー、レンジツリーの違いは何ですか?
-
[解決済み】固定長 6 int 配列の最速ソート