web-dev-qa-db-fra.com

Algorithme somme sous-ensemble

Je travaille sur ce problème:

Le problème Sous-ensemble Somme prend en entrée un ensemble X = {x1, x2 ,…, xn} d'entiers n et un autre entier K. Le problème est de vérifier s’il existe un sous-ensemble X' de X dont la somme des éléments est K et trouve le sous-ensemble s’il en existe. Par exemple, si X = {5, 3, 11, 8, 2} et K = 16, la réponse est YES puisque le sous-ensemble X' = {5, 11} a la somme de 16. Implémentez un algorithme pour Subset Sum dont le temps d'exécution est au moins O(nK).

Remarquez la complexité O(nK). Je pense que la programmation dynamique peut aider.

J'ai trouvé un algorithme de temps exponentiel, mais ça n'aide pas.

Quelqu'un peut-il m'aider à résoudre ce problème?

36
The expendable

Comme il semble que tous vos chiffres sont positifs, vous pouvez résoudre ce problème en utilisant la programmation dynamique:

Start sera un tableau booléen possible de taille K + 1 avec la première valeur true, le reste faux. La ième valeur déterminera si une somme de i est possible à réaliser. Pour chaque nombre n de votre ensemble, parcourez le tableau possible. Si la ith valeur est true, définissez également la i + nième valeur sur true.

À la fin, si la kième valeur de possible est vraie, vous pouvez alors former une somme de k. Problème résolu dans le temps O(NK).

La page de Wikipédia sur le problème de la somme des sous-ensembles contient une explication détaillée de cet algorithme appliquée aux ensembles d'entiers non garantis positifs.

18
marcog

Je suggère de lire l'algorithme de Wiki . L'algorithme existe ici, voir Solution de programmation dynamique du temps pseudo-polynomial pour la solution O(P*n), La solution n'est pas du temps polynomial, est polynomiale dans (p, n) mais n'est pas polynomiale dans n + log P (taille de l'entrée ) et parce que P peut être très grand comme 2 ^ n, la solution P * n = (2 ^ n) * n n’est pas une solution temporelle polynomiale en général, mais lorsque p est lié par une fonction polynomiale de n, l’algorithme polynomial de temps .

Ce problème est NPC, mais il existe un algorithme Pseudo polynomial time pour celui-ci, et appartient à weakly NP-Complete problèmes, il y a aussi Strongly NP-Complete problèmes, ce qui signifie que vous ne pouvez trouver aucun algorithme pseudo polynomial time pour eux à moins de P = NP , et ce problème ne se trouve pas dans cette gamme de problèmes, donc, en quelque sorte, est facile.

J'ai dit cela aussi simplement que possible, mais ce n'est pas une définition exacte d'un problème Fortement NP-Complet ou Faiblement NP-Complet.

Pour plus de détails, voir Garey et Johnson chapitre 4.

8
Saeed Amiri

Il semble que je sois en retard à la fête, voici mes deux centimes. Nous allons créer un boolean[] solution[n+1][k+1] tel que solution[i][j] est true si vous utilisez les premiers éléments i (index 0 à i-1), nous pouvons obtenir la somme j de l'ensemble; sinon false. Nous retournerons solution[k][n] enfin:

On peut en déduire les points suivants:

  1. si somme vaut zéro, alors toujours une réponse possible (ensemble vide) pour un nombre quelconque d'éléments. Donc tout est vrai.
  2. si set est vide, nous ne pouvons avoir aucun sous-ensemble, donc aucun moyen d'obtenir un K. Donc, jamais une réponse possible. Tout faux.
  3. si un sous-ensemble X1 (sous-ensemble de X sans dernier élément dans X) a un sous-ensemble pour k, alors X l'a aussi, qui est X1. Par exemple. pour X1 = {1,3,5} et k = 8, si X1 a une somme partielle alors X = {1,3,5,7} a aussi une somme partielle
  4. Pour i/p, définir X = {1,3,5,7,19} et k = 20, si X veut connaître la possibilité d'une sous-somme pour 20, il le fera si x1 = {1,3,5,7} peut avoir une sous-somme de 20-19 c'est-à-dire 1. Cela ne s'applique que si k> = 19, c'est-à-dire le dernier élément de X.

Sur la base des points ci-dessus, nous pouvons facilement écrire l'algorithme ci-dessous. 

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
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

Il n'y a pas d'algorithme connu pour la somme de sous-ensembles qui s'exécute dans moins de O (2 ^ (n/2)), dans le cas général.

2
Puppy

Solution DP avec un tableau à une dimension (l’ordre de traitement de la matrice DP importe ici).

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

Les réponses ci-dessus sont toutes excellentes, mais ne donnez pas le plus grand aperçu de la manière dont cela pourrait fonctionner pour les nombres positifs et négatifs.

Étant donné un ensemble ordonné d’entiers, définissez deux variables X et Y telles que

X = somme des éléments négatifs

Y = somme des éléments positifs

et opérez sur votre ensemble initial comme si vous récursiez dans un arbre binaire en appliquant ces règles dans cet ordre

  1. Si l'élément le plus à droite est égal à la somme que vous essayez de vérifier For return true 
  2. Recurse à gauche si cela ne laissait pas le jeu vide , Déposez l'élément le plus à droite de votre tableau trié 
  3. S'il reste un élément dans votre ensemble et que ce n'est pas la somme, le retour est faux
  4. Au lieu de revenir à droite, vérifiez la somme de tous les éléments du tableau Q, si X <= B <= Y, renvoyez la valeur true, sinon la valeur false 
  5. Si la sous-arborescence de gauche ou la ‘récursivité’ de droite a renvoyé la valeur true, retournez la valeur true au parent

Les réponses ci-dessus sont plus détaillées et précises, mais pour une vue très large de la façon dont cela devrait se dérouler, dessinez un arbre binaire. Qu'est-ce que la longueur de ceci suggère sur le runtime?

0
Legion Daeth

soit M la somme de tous les éléments. Notez que 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]];

Puis testez simplement 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));
}

Considérons le ième élément. Soit il contribuera à la somme du sous-ensemble ou ne le sera pas. si elle contribue pour la somme, la "valeur de la somme" est diminuée de la valeur égale au ième élément. S'il ne contribue pas, nous devons alors rechercher la "valeur de la somme" dans les éléments restants. 

0
Mostafizar