web-dev-qa-db-fra.com

trouver tous les sous-ensembles qui totalisent une valeur particulière

Pour un ensemble de nombres: {1, 3, 2, 5, 4, 9}, trouvez le nombre de sous-ensembles qui totalisent une valeur particulière (par exemple 9 pour cet exemple).

Ceci est similaire au problème de somme de sous-ensembles avec la légère différence qui consiste à rechercher le nombre de tels sous-ensembles au lieu de vérifier si l'ensemble a un sous-ensemble égal à 9. Je suis la solution pour le problème somme somme ici . Mais je me demande comment je peux le modifier pour renvoyer le nombre de sous-ensembles.

39
Darth.Vader
def total_subsets_matching_sum(numbers, sum):
    array = [1] + [0] * (sum)
    for current_number in numbers:
        for num in xrange(sum - current_number, -1, -1):
            if array[num]:
                array[num + current_number] += array[num]
    return array[sum]

assert(total_subsets_matching_sum(range(1, 10), 9)       == 8)
assert(total_subsets_matching_sum({1, 3, 2, 5, 4, 9}, 9) == 4)

Explication

C'est l'un des problèmes classiques. L'idée est de trouver le nombre de sommes possibles avec le nombre actuel. Et il est vrai que, il y a exactement un moyen de ramener la somme à 0. Au début, nous n'avons qu'un seul nombre. Nous partons de notre cible (variable Maximum dans la solution) et soustrayons ce nombre. S'il est possible d'obtenir une somme de ce nombre (l'élément de tableau correspondant à ce nombre n'est pas nul), ajoutez-le à l'élément de tableau correspondant au nombre actuel. Le programme serait plus facile à comprendre de cette façon

for current_number in numbers:
    for num in xrange(sum, current_number - 1, -1):
        if array[num - current_number]:
            array[num] += array[num - current_number]

Lorsque le nombre est égal à 1, il n’ya qu’un seul moyen de calculer la somme de 1 (1-1 devient 0 et l’élément correspondant à 0 est 1). Donc, le tableau serait comme ceci (rappelez-vous que l'élément zéro aura 1)

[1, 1, 0, 0, 0, 0, 0, 0, 0, 0]

Maintenant, le deuxième nombre est 2. Nous commençons à soustraire 2 de 9 et son non valide (puisque l’élément de tableau de 7 est zéro nous le sautons) nous continuons à le faire jusqu’à 3. Lorsque son 3, 3 - 2 est 1 et que correspondant à 1 est 1 et nous l'ajoutons à l'élément de tableau de 3. et lorsque ses 2, 2 - 2 devient 0 et nous la valeur correspondant à 0 à l'élément de tableau de 2. Après cette itération, le tableau ressemble à ceci

[1, 1, 1, 1, 0, 0, 0, 0, 0, 0]

Nous continuons à faire cela jusqu'à ce que nous traitions tous les nombres et le tableau après chaque itération ressemble à ceci

[1, 1, 0, 0, 0, 0, 0, 0, 0, 0]
[1, 1, 1, 1, 0, 0, 0, 0, 0, 0]
[1, 1, 1, 2, 1, 1, 1, 0, 0, 0]
[1, 1, 1, 2, 2, 2, 2, 2, 1, 1]
[1, 1, 1, 2, 2, 3, 3, 3, 3, 3]
[1, 1, 1, 2, 2, 3, 4, 4, 4, 5]
[1, 1, 1, 2, 2, 3, 4, 5, 5, 6]
[1, 1, 1, 2, 2, 3, 4, 5, 6, 7]
[1, 1, 1, 2, 2, 3, 4, 5, 6, 8]

Après la dernière itération, nous aurions considéré tous les nombres et le nombre de manières d'obtenir la cible serait l'élément de tableau correspondant à la valeur cible. Dans notre cas, Array [9] après la dernière itération est 8.

31
thefourtheye

Vous pouvez utiliser la programmation dynamique. La complexité de l'algo est O (Sum * N) et utilisez O(Sum) memory.

Voici mon implémentation en C #:

private static int GetmNumberOfSubsets(int[] numbers, int sum)
{
    int[] dp = new int[sum + 1];
    dp[0] = 1;
    int currentSum =0;
    for (int i = 0; i < numbers.Length; i++)
    {
        currentSum += numbers[i];
        for (int j = Math.Min(sum, currentSum); j >= numbers[i]; j--)
            dp[j] += dp[j - numbers[i]];
    }

    return dp[sum];
}

Notes : Le nombre de sous-ensembles pouvant avoir la valeur 2 ^ N, il est possible que le nombre débordant de type soit trop élevé.

Algo ne fonctionne que pour les nombres positifs.

15
Толя

Voici un Java Solution:

Il s’agit d’un problème classique de suivi pour rechercher tous les sous-ensembles possibles du tableau entier ou de l’ensemble constituant l’entrée, puis filtering ceux qui totalisent e target

import Java.util.HashSet;
import Java.util.StringTokenizer;

/**
 * Created by anirudh on 12/5/15.
 */
public class findSubsetsThatSumToATarget {

    /**
     * The collection for storing the unique sets that sum to a target.
     */
    private static HashSet<String> allSubsets = new HashSet<>();

    /**
     * The String token
     */
    private static final String token = " ";

    /**
     * The method for finding the subsets that sum to a target.
     *
     * @param input  The input array to be processed for subset with particular sum
     * @param target The target sum we are looking for
     * @param ramp   The Temporary String to be beefed up during recursive iterations(By default value an empty String)
     * @param index  The index used to traverse the array during recursive calls
     */
    public static void findTargetSumSubsets(int[] input, int target, String ramp, int index) {

        if(index > (input.length - 1)) {
            if(getSum(ramp) == target) {
                allSubsets.add(ramp);
            }
            return;
        }

        //First recursive call going ahead selecting the int at the currenct index value
        findTargetSumSubsets(input, target, ramp + input[index] + token, index + 1);
        //Second recursive call going ahead WITHOUT selecting the int at the currenct index value
        findTargetSumSubsets(input, target, ramp, index + 1);
    }

    /**
     * A helper Method for calculating the sum from a string of integers
     *
     * @param intString the string subset
     * @return the sum of the string subset
     */
    private static int getSum(String intString) {
        int sum = 0;
        StringTokenizer sTokens = new StringTokenizer(intString, token);
        while (sTokens.hasMoreElements()) {
            sum += Integer.parseInt((String) sTokens.nextElement());
        }
        return sum;
    }

    /**
     * Cracking it down here : )
     *
     * @param args command line arguments.
     */
    public static void main(String[] args) {
        int [] n =  {24, 1, 15, 3, 4, 15, 3};
        int counter = 1;
        FindSubsetsThatSumToATarget.findTargetSumSubsets(n, 25, "", 0);
        for (String str: allSubsets) {
            System.out.println(counter + ") " + str);
            counter++;
        }
    }
}

Il donne des valeurs séparées par des espaces des sous-ensembles qui totalisent une cible.

Pourrait imprimer les valeurs séparées par des valeurs commma pour les sous-ensembles qui totalisent 25 en {24, 1, 15, 3, 4, 15, 3}

1) 24 1 

2) 3 4 15 3 

3) 15 3 4 3 

10
Anirudh

Le même site geeksforgeeks discute également de la solution permettant d’afficher tous les sous-ensembles d’une somme donnée: http://www.geeksforgeeks.org/backttracking-set-4-subset-sum/

Dans votre cas, au lieu des jeux de sortie, il vous suffit de les compter. Assurez-vous de vérifier la version optimisée sur la même page, car il s’agit d’un problème NP-complete .

Cette question a également été posée et répondue auparavant dans stackoverflow sans mentionner qu'il s'agit d'un problème de sous-ensemble: Trouver toutes les combinaisons possibles de nombres pour atteindre une somme donnée

6
kaisernahid

C'est mon programme en rubis. Il retournera des tableaux, chacun contenant les sous-séquences qui totalisent la valeur cible fournie.

array = [1, 3, 4, 2, 7, 8, 9]

0..array.size.times.each do |i| 
  @ary.combination(i).to_a.each { |a| print a if a.inject(:+) == 9} 
end
5
Ritesh Ranjan

J'ai résolu ceci par Java. Cette solution est assez simple.

import Java.util.*;

public class Recursion {

static void sum(int[] arr, int i, int sum, int target, String s)
{   
    for(int j = i+1; j<arr.length; j++){
        if(sum+arr[j] == target){
            System.out.println(s+" "+String.valueOf(arr[j]));
        }else{
            sum(arr, j, sum+arr[j], target, s+" "+String.valueOf(arr[j]));
        }
    }
}

public static void main(String[] args)
{   
    int[] numbers = {6,3,8,10,1};
    for(int i =0; i<numbers.length; i++){
        sum(numbers, i, numbers[i], 18, String.valueOf(numbers[i])); 
    }

}
}
4
Townim Faisal

La solution de DP habituelle est vraie pour le problème. 

Une optimisation que vous pouvez faire est de garder un compte du nombre de solutions existantes pour la somme particulière plutôt que des ensembles qui composent cette somme.

2
Kshitij Banerjee

Ceci est mon implémentation de programmation dynamique dans JS. Il renverra un tableau de tableaux, chacun contenant les sous-séquences qui totalisent la valeur cible fournie.

function getSummingItems(a,t){
  return a.reduce((h,n) => Object.keys(h)
                                 .reduceRight((m,k) => +k+n <= t ? (m[+k+n] = m[+k+n] ? m[+k+n].concat(m[k].map(sa => sa.concat(n)))
                                                                                      : m[k].map(sa => sa.concat(n)),m)
                                                                 :  m, h), {0:[[]]})[t];
}
var arr = Array(20).fill().map((_,i) => i+1), // [1,2,..,20]
    tgt = 42,
    res = [];

console.time("test");
res = getSummingItems(arr,tgt);
console.timeEnd("test");
console.log("found",res.length,"subsequences summing to",tgt);
console.log(JSON.stringify(res));

2
Redu

Rubis

Ce code rejettera les tableaux vides et retournera le tableau approprié avec les valeurs.

def find_sequence(val, num)
  b = val.length
  (0..b - 1).map {|n| val.uniq.combination(n).each.find_all {|value| value.reduce(:+) == num}}.reject(&:empty?)
end

val = [-10, 1, -1, 2, 0]
num = 2

La sortie sera [[2], [2,0], [- 1,1,2], [- 1,1,2,0]]

0
Preethi

La solution suivante fournit également un tableau de sous-ensembles fournissant une somme spécifique (ici somme = 9)

array = [1, 3, 4, 2, 7, 8, 9]

(0..array.size).map { |i| array.combination(i).to_a.select { |a| a.sum == 9 } }.flatten(1)

retour tableau de sous-ensembles qui retournent la somme de 9

 => [[9], [1, 8], [2, 7], [3, 4, 2]] 
0
ray
public class SumOfSubSet {

    public static void main(String[] args) {
        // TODO Auto-generated method stub

        int a[] = {1,2};
        int sum=0;
        if(a.length<=0) {
            System.out.println(sum);
        }else {
        for(int i=0;i<a.length;i++) {
            sum=sum+a[i];
            for(int j=i+1;j<a.length;j++) {
                sum=sum+a[i]+a[j];
            }
        }
        System.out.println(sum);

        }


    }

}
0
satish

Ma solution de retour en arrière: - Trier le tableau, puis appliquer le retour en arrière.

void _find(int arr[],int end,vector<int> &v,int start,int target){
        if(target==0){
        for(int i = 0;i<v.size();i++){
            cout<<v[i]<<" ";
        }
        cout<<endl;
    }

    else{
        for(int i =  start;i<=end && target >= arr[i];i++){
            v.Push_back(arr[i]);
            _find(arr,end,v,i+1,target-arr[i]);
            v.pop_back();
        }
    }
}
0
HeadAndTail

Bien qu'il soit facile de savoir si leur sous-ensemble correspond ou non à la cible, la mise en œuvre devient délicate lorsque vous devez suivre les sous-ensembles partiels à l'étude.

Si vous utilisez une liste liée, un ensemble de hachage ou toute autre collection générique, vous seriez tenté d'ajouter un élément à cette collection avant l'appel qui inclut l'élément, puis de le supprimer avant l'appel qui l'exclut. Cela ne fonctionne pas comme prévu, car les images de pile dans lesquelles l’ajout aura lieu ne sont pas les mêmes que celles dans lesquelles l’élimination aura lieu.

La solution consiste à utiliser une chaîne pour garder une trace de la séquence. Les ajouts à la chaîne peuvent être effectués en ligne dans l'appel de fonction; maintenant ainsi le même cadre de pile et votre réponse se conformerait alors magnifiquement à la structure récursive hasSubSetSum d'origine.

import Java.util.ArrayList;

classe publique Solution {

public static boolean hasSubSet(int [] A, int target) {
    ArrayList<String> subsets = new ArrayList<>();
    helper(A, target, 0, 0, subsets, "");
    // Printing the contents of subsets is straightforward
    return !subsets.isEmpty();
}

private static void helper(int[] A, int target, int sumSoFar, int i, ArrayList<String> subsets, String curr) {
    if(i == A.length) {
        if(sumSoFar == target) {
            subsets.add(curr);
        }
        return;
    }
    helper(A, target, sumSoFar, i+1, subsets, curr);
    helper(A, target, sumSoFar + A[i], i+1, subsets, curr + A[i]);
}

public static void main(String [] args) {
    System.out.println(hasSubSet(new int[] {1,2,4,5,6}, 8));
}

}

0
Azeem

Le problème de la somme des sous-ensembles peut être résolu dans O (sum * n) en utilisant la programmation dynamique. La sous-structure optimale pour la somme de sous-ensembles est la suivante:

SubsetSum (A, n, sum) = SubsetSum (A, n-1, sum) || SubsetSum (A, n-1, somme-set [n-1])

SubsetSum (A, n, sum) = 0, si sum> 0 et n == 0 SubsetSum (A, n, sum) = 1, si sum == 0 

IciAest un tableau d'éléments, n est le nombre d'éléments du tableau A et sum est la somme des éléments du sous-ensemble.

En utilisant ce dp, vous pouvez résoudre le nombre de sous-ensembles pour la somme.

Pour obtenir des éléments de sous-ensemble, nous pouvons utiliser l'algorithme suivant:

Après avoir rempli dp [n] [somme] en appelant SubsetSum (A, n, somme), nous le parcourons récursivement à partir de dp [n] [somme]. Pour la cellule traversée, nous stockons chemin avant de l’atteindre et considérons deux possibilités pour l’élément.

1) L'élément est inclus dans le chemin actuel.

2) L'élément n'est pas inclus dans le chemin actuel.

Chaque fois que la somme devient 0, nous arrêtons les appels récursifs et imprimons le chemin actuel.

void findAllSubsets(int dp[], int A[], int i, int sum, vector<int>& p) { 

   if (sum == 0) { 
        print(p); 
        return; 
   } 

   // If sum can be formed without including current element
   if (dp[i-1][sum]) 
   { 
        // Create a new vector to store new subset 
        vector<int> b = p; 
        findAllSubsets(dp, A, i-1, sum, b); 
   } 

   // If given sum can be formed after including 
   // current element. 
   if (sum >= A[i] && dp[i-1][sum-A[i]]) 
   { 
        p.Push_back(A[i]); 
        findAllSubsets(dp, A, i-1, sum-A[i], p); 
   } 

} 
0
Himanshu Kansal