web-dev-qa-db-ja.com

サブセット和アルゴリズム

私はこの問題に取り組んでいます:

サブセット合計問題は、入力としてn整数と別の整数Kのセット_X = {x1, x2 ,…, xn}_を取ります。問題は、要素の合計がXになり、サブセットがある場合はそのサブセットを見つけるKのサブセット_X'_が存在するかどうかを確認することです。たとえば、_X = {5, 3, 11, 8, 2}_および_K = 16_の場合、サブセット_X' = {5, 11}_の合計は_16_であるため、答えはYESです。実行時間が少なくともO(nK)であるSubset Sumのアルゴリズムを実装します。

複雑さO(nK)に注意してください。動的プログラミングが役立つと思います。

指数時間アルゴリズムを見つけましたが、それは役に立ちません。

誰かがこの問題の解決を手伝ってくれますか?

43
The expendable

すべての数値が正であるように見えるため、動的プログラミングを使用してこれを解決できます。

Startは、サイズK + 1のブール配列possibleを最初の値がtrue、残りがfalseであるようにします。 i番目の値は、iのサブセット合計を達成できるかどうかを表します。セット内の各数値nについて、possible配列をループし、i番目の値がtrueの場合、i + n番目の値もtrueに設定します。

最後に、possibleのk番目の値がtrueの場合、kのサブセット合計を形成できます。問題はO(NK)時間で解決しました。

サブセット和問題に関するウィキペディアのページ には、正であることが保証されていない整数のセットに適用されるこのアルゴリズムの詳細な説明があります。

18
marcog

Wiki のアルゴリズムを読むことをお勧めします。アルゴリズムはそこに存在します。を参照してください。O(P*n)解の擬似多項式時間動的計画法解、解は多項式時間ではなく、 (p、n)の多項式ですが、n + log P(入力のサイズ)の多項式ではなく、Pは2 ^ nのように非常に大きくなる可能性があるため、解P * n =(2 ^ n)* nは一般に多項式時間解ではありませんが、pがnの多項式関数によって制限される場合、多項式時間アルゴリズムになります。

この問題はNPCですが、Pseudo polynomial timealgorithmであり、 weakly NP-Complete 問題、 Strongly NP-Complete 問題。つまり、pseudo polynomial time P = NPでない限り、それらのアルゴリズムであり、この問題はこの範囲の問題ではないため、どういうわけか簡単です。

私はこれをできるだけ簡単に述べましたが、それは厳密にNP完全な問題または弱いNP完全な問題の正確な定義ではありません。

詳細については、 Garey and Johnson 4章を参照してください。

8
Saeed Amiri

私はパーティーに遅れているようです、ここに私の2セントがあります。最初のtrueアイテム(インデックスboolean[] solution[n+1][k+1]からsolution[i][j])を使用する場合、0iになるようなi-1を作成します。セットからjの合計を取得できます。それ以外の場合はfalse。最後にsolution[k][n]を返します:

次の点を推測できます。

  1. sumがゼロの場合、任意の数の要素に対して常に可能な回答(空のセット)になります。それですべて真実。
  2. セットが空の場合、サブセットを取得できないため、Kを取得する方法がありません。可能な回答はありません。すべて偽。
  3. サブセットX1(Xの最後の要素を含まないXのサブセット)にkのサブセット和がある場合、XにもX1があります。例えば。 X1 = {1,3,5}およびk = 8の場合、X1にサブセット合計がある場合、X = {1,3,5,7}にもサブセット合計がある
  4. I/pの場合、X = {1,3,5,7,19}およびk = 20を設定します。Xが20のサブセット和の可能性を知りたい場合、x1 = {1,3,5,7}の場合20-19すなわち1の部分集合和を持つことができます。k> = 19の場合、つまりXの最後の要素の場合にのみ適用されます。

上記のポイントに基づいて、次のようにアルゴリズムを簡単に書くことができます。

public class SubSetSum {
    boolean[][] solution; 
    int[] input;
    int k;

    public SubSetSum(int[] input, int targetSum) {
        this.input = input;
        this.k = targetSum;
        this.solution = new boolean[input.length+1][k+1];
    }

    public boolean subsetSum() {
        int n = input.length;

        for (int i = 0; i <= n; i++) {     //case 1
            solution[i][0] = true;
        }

        for (int j = 0; j <= k; j++) {    // case 2
            solution[0][j] = false;
        }

        for (int i = 1; i <= n; i++) {                  // n times
            for (int j = 1; j <= k; j++) {              // k times and time complexity O(n*k)
                if(solution[i-1][j]) {
                    solution[i][j] = solution[i-1][j];      // case 3
                    continue;
                }
                if(j >= input[i-1])  {                       // case 4
                    solution[i][j] = solution[i-1][j-input[i-1]];
                }
            }
        }
        return solution[n][k];
    }
}
3
i_am_zero

一般的な場合、O(2 ^(n/2))未満で実行されるサブセット合計の既知のアルゴリズムはありません。

3
Puppy
void subsetSum (int arr[], int size, int target) {
  int i, j ;
  int **table ;
  table = (int **) malloc (sizeof(int*) * (size+1)) ;
  for ( i = 0 ; i <= size ; i ++ ) {
    table[i] = (int *) malloc (sizeof(int) * (target+1)) ;
    table[i][0] = 1 ;
  }
  for ( j = 1 ; j <= target ; j ++ )
    table[0][j] = 0 ;
  for ( i = 1 ; i <= size ; i ++ ) {
    for ( j = 1 ; j <= target ; j ++ )
      table[i][j] = table[i-1][j] || (arr[i-1] <= j && table[i-1][j-arr[i-1]] ) ;
  } 
  if ( table[size][target] == 1 )
    printf ( "\ntarget sum found\n" ) ; 
  else printf ( "\nTarget sum do not found!\n" ) ;
  free (table) ;
}
2
Psycho

1次元配列のDPソリューション(ここでは、DP配列の処理順序が重要です)。

bool subsetsum_dp(vector<int>& v, int sum)
{
    int n = v.size();
    const int MAX_ELEMENT = 100;
    const int MAX_ELEMENT_VALUE = 1000;
    static int dp[MAX_ELEMENT*MAX_ELEMENT_VALUE + 1]; memset(dp, 0, sizeof(dp));

    dp[0] = 1;

    for (int i = 0; i < n; i++)
    {
        for (int j = MAX_ELEMENT*MAX_ELEMENT_VALUE; j >= 0; j--)
        {
            if (j - v[i] < 0) continue;
            if (dp[j - v[i]]) dp[j] = 1; 
        }
    }

    return dp[sum] ? true : false;
}
0
CodingLab

N ^ 2時間の複雑さを伴う再帰的ソリューション

public void solveSubsetSum(){
    int set[] = {2,6,6,4,5};
            int sum = 9;
            int n = set.length;

            // check for each element if it is a part of subset whose sum is equal to given sum
            for (int i=0; i<n;i++){
                if (isSubsetSum(set, sum, i, n)){
                    Log.d("isSubset:", "true") ;
                    break;
                }
                else{
                    Log.d("isSubset:", "false") ;
                }
                k=0; // to print time complexity pattern
            }
        }

private boolean isSubsetSum(int[] set, int sum, int i, int n) {

            for (int l=0;l<k; l++){
            System.out.print("*"); 
            // to print no of time is subset call for each element
        }
        k++;
        System.out.println();     
        if (sum == 0){
            return true;
        }

        if (i>=n){
            return false;
        }

        if (set[i] <= sum){ 
        // current element is less than required sum then we have to check if rest of the elements make a subset such that its sum is equal to the left sum(sum-current element)
            return isSubsetSum(set, sum-set[i], ++i, n);
        }
        else { //if current element is greater than required sum
            return isSubsetSum(set, sum, ++i, n);
        }
   }

最悪の場合の複雑さ:O(n ^ 2)

ベストケース:O(n)つまり、最初の要素が、合計が指定された合計に等しいサブセットを作成する場合。

ここで時間の複雑さを計算するのが間違っている場合は修正してください。

0
Vishakha Tyagi

上記の答えはすべて素晴らしいですが、実際にこのようなものが正と負の数の両方でどのように機能するかについての最も広い概要を示していません。

整数の順序付きセットが与えられた場合、次のように2つの変数XとYを定義します

X =負の要素の合計

Y =正の要素の合計

これらのルールをこの順序で適用することにより、バイナリツリーを再帰処理するかのように初期セットを操作します

  1. 右端の要素が、trueを返すかどうかを確認しようとしている合計と等しい場合
  2. 空のセットを残さない場合は左に再帰し、ソートされた配列から右端の要素をドロップします
  3. セットに1つの要素が残っていて、合計ではない場合はfalseを返します
  4. 右に再帰する代わりに、配列q内のすべての要素の合計を確認します。X<= B <= Yの場合はtrueを返し、falseを返さない場合は
  5. 左のサブツリーまたは右の「再帰」がtrueを返した場合、親にtrueを返します

上記の答えはより詳細で正確ですが、これがどのように展開されるべきかについての非常に広い視野のために、バイナリツリーを描きます。この長さはランタイムについて何を示唆していますか?

0
Legion Daeth

mをすべての要素の合計とします。 K <= M

let m be a Boolean array [0...M]
set all elements of m to be False
m[0]=1
for all numbers in the set let a[i] be the ith number
    for j = M to a[i]
        m[j] = m[j] | m[j-a[i]];

その後、単純にm [k]をテストします

0
MuhammadKhalifa
boolean hasSubset(int arr[],int remSum,int lastElem){
    if(remSum==0) return true;
    else if(remSum!=0 && lastElem<0) return false;

    if(arr[lastElem]>remSum) return hasSubset(arr, remSum, lastElem-1);
    else return (hasSubset(arr, remSum, lastElem-1) ||hasSubset(arr, remSum-arr[lastElem], lastElem-1));
}

I番目の要素を考えます。サブセットの合計に貢献するか、貢献しません。合計に寄与する場合、「合計の値」はi番目の要素に等しい値だけ減少します。寄与しない場合、残りの要素で「合計の値」を検索する必要があります。

0
Mostafizar
function subsetsum(a, n) {
    var r = [];
    for (var i = parseInt(a.map(function() { return 1 }).join(''), 2); i; i--) {
        var b = i.toString(2).split('').reverse().map(function(v, i) {
            return Number(v) * a[i]
        }).filter(Boolean);
        if (eval(b.join('+')) == n) r.Push(b);
    }
    return r;
}

var a = [5, 3, 11, 8, 2];
var n = 16;
console.log(subsetsum(a, n)); // -> [[3, 11, 2], [5, 3, 8], [5, 11]]

ブルートフォース-ソートを忘れて、すべてのコンボを試してください。evalパーサーはArray.reduceに勝っています(そして、負の数でも動作します)。

0
Rick Edwards