Comment feriez-vous pour tester toutes les combinaisons possibles d'additions à partir d'un ensemble donné N
de nombres afin qu'ils s'additionnent à un nombre final donné?
Un bref exemple:
- Ensemble de nombres à ajouter:
N = {1,5,22,15,0,...}
- Résultat désiré:
12345
Réponses:
Ce problème peut être résolu avec une combinaison récursive de toutes les sommes possibles filtrant celles qui atteignent la cible. Voici l'algorithme en Python:
Ce type d'algorithmes est très bien expliqué dans la conférence suivante de programmation abstraite de Standford - cette vidéo est très recommandable pour comprendre comment fonctionne la récursivité pour générer des permutations de solutions.
Éditer
Ce qui précède en tant que fonction de générateur, ce qui le rend un peu plus utile. Nécessite Python 3.3+ à cause de
yield from
.Voici la version Java du même algorithme:
C'est exactement la même heuristique. Mon Java est un peu rouillé mais je pense qu'il est facile à comprendre.
Conversion C # de la solution Java: (par @JeremyThompson)
Solution Ruby: (par @emaillenin)
Edit: discussion de complexité
Comme d'autres le mentionnent, c'est un problème NP-difficile . Il peut être résolu en temps exponentiel O (2 ^ n), par exemple pour n = 10 il y aura 1024 solutions possibles. Si les cibles que vous essayez d'atteindre sont dans une fourchette basse, alors cet algorithme fonctionne. Ainsi, par exemple:
subset_sum([1,2,3,4,5,6,7,8,9,10],100000)
génère 1024 branches car la cible ne parvient jamais à filtrer les solutions possibles.En revanche,
subset_sum([1,2,3,4,5,6,7,8,9,10],10)
ne génère que 175 branches, car la cible à atteindre10
doit filtrer de nombreuses combinaisons.Si
N
etTarget
sont de grands nombres, on devrait passer à une version approximative de la solution.la source
[1, 2, 0, 6, -3, 3], 3
- il ne sort[1,2], [0,3], [3]
que lorsque des cas manquants tels que[6, -3, 3]
[1, 2, 5], 5
seulement les sorties[5]
, quand[1, 1, 1, 1, 1]
,[2, 2, 1]
et[2, 1, 1, 1]
sont des solutions.i+1
inremaining = numbers[i+1:]
. Il semble que cet algorithme n'autorise pas les doublons.[1, 1, 3]
jetez un œil à stackoverflow.com/a/34971783/3684296 (Python)La solution de ce problème a été donnée un million de fois sur Internet. Le problème est appelé le problème de changement de pièce . On peut trouver des solutions sur http://rosettacode.org/wiki/Count_the_coins et un modèle mathématique de celui-ci sur http://jaqm.ro/issues/volume-5,issue-2/pdfs/patterson_harmel.pdf (ou changement de pièce Google problème ).
Soit dit en passant, la solution Scala de Tsagadai est intéressante. Cet exemple produit 1 ou 0. Comme effet secondaire, il répertorie sur la console toutes les solutions possibles. Il affiche la solution, mais échoue à la rendre utilisable de quelque manière que ce soit.
Pour être le plus utile possible, le code doit renvoyer un
List[List[Int]]
afin de permettre d'obtenir le nombre de solutions (longueur de la liste des listes), la "meilleure" solution (la liste la plus courte), ou toutes les solutions possibles.Voici un exemple. C'est très inefficace, mais c'est facile à comprendre.
Lorsqu'il est exécuté, il affiche:
La
sumCombinations()
fonction peut être utilisée seule et le résultat peut être analysé plus en détail pour afficher la «meilleure» solution (la liste la plus courte) ou le nombre de solutions (le nombre de listes).Notez que même ainsi, les exigences peuvent ne pas être entièrement satisfaites. Il peut arriver que l'ordre de chaque liste dans la solution soit significatif. Dans un tel cas, chaque liste devrait être dupliquée autant de fois qu'il y a de combinaison de ses éléments. Ou nous pourrions être intéressés uniquement par les combinaisons qui sont différentes.
Par exemple, nous pourrions considérer que cela
List(5, 10)
devrait donner deux combinaisons:List(5, 10)
etList(10, 5)
. CarList(5, 5, 5)
il pourrait donner trois combinaisons ou une seule, selon les besoins. Pour les entiers, les trois permutations sont équivalentes, mais si nous avons affaire à des pièces, comme dans le "problème de changement de pièces", elles ne le sont pas.Il n'est pas non plus indiqué dans les exigences la question de savoir si chaque numéro (ou pièce) ne peut être utilisé qu'une ou plusieurs fois. Nous pourrions (et nous devrions!) Généraliser le problème à une liste de listes d'occurrences de chaque nombre. Cela se traduit dans la vie réelle par "quelles sont les façons possibles de gagner une certaine somme d'argent avec un ensemble de pièces (et non un ensemble de valeurs de pièces)". Le problème d'origine n'est qu'un cas particulier de celui-ci, où nous avons autant d'occurrences de chaque pièce que nécessaire pour faire le montant total avec chaque valeur de pièce unique.
la source
À Haskell :
Et J :
Comme vous pouvez le remarquer, les deux adoptent la même approche et divisent le problème en deux parties: générer chaque membre de l'ensemble de puissance et vérifier la somme de chaque membre à la cible.
Il existe d'autres solutions, mais c'est la plus simple.
Avez-vous besoin d'aide pour l'une ou l'autre ou pour trouver une approche différente?
la source
not in scope: 'subsequences'
des pointeurs?import Data.List
Une version Javascript:
la source
remaining = numbers.slice();
remaining.slice(i + 1);
autrementnumbers.slice(i + 1);
change le tableau des nombresslice
renvoie une copie (superficielle), il ne modifie pas lenumbers
tableau.Version C ++ du même algorithme
la source
Version C # de la réponse du code @msalvadores
la source
Je pensais que j'utiliserais une réponse à cette question mais je ne pouvais pas, alors voici ma réponse. Il utilise une version modifiée d'une réponse dans Structure et interprétation des programmes informatiques . Je pense que c'est une meilleure solution récursive et devrait plaire davantage aux puristes.
Ma réponse est en Scala (et je m'excuse si mon Scala est nul, je viens de commencer à l'apprendre). La folie de findSumCombinations est de trier et unique la liste originale pour la récursivité pour éviter les dupes.
Pour l'utiliser:
la source
j'ai converti la logique ci-dessus de python en php ..
la source
Une autre solution python serait d'utiliser le
itertools.combinations
module comme suit:Production:
[(8, 7), (5, 10), (3, 8, 4), (3, 5, 7)]
la source
Voici une solution dans R
la source
subset_sum(numbers = c(1:2), target = 5)
renvoie"sum(1+2+2)=5"
. Mais la combinaison 1 + 1 + 1 + 1 + 1 est manquante. Fixer des cibles à des nombres plus élevés (par exemple 20) manque encore plus de combinaisons.subset_sum(1:2, 4)
ne devrait retourner aucune solution car il n'y a pas de combinaison de 1 et 2 qui ajoute à 4. Ce qui doit être ajouté à ma fonction est une fuite sii
est supérieure à la longueur denumbers
Voici une version Java qui convient bien aux petits N et aux très grandes sommes cibles, lorsque la complexité
O(t*N)
(la solution dynamique) est supérieure à l'algorithme exponentiel. Ma version utilise une rencontre dans l'attaque du milieu, avec un peu de décalage afin de réduire la complexité du classique naïfO(n*2^n)
auO(2^(n/2))
.Si vous souhaitez l'utiliser pour des ensembles comprenant entre 32 et 64 éléments, vous devez changer le
int
qui représente le sous-ensemble actuel dans la fonction pas à unlong
bien que les performances diminuent de manière drastique à mesure que la taille de l'ensemble augmente. Si vous souhaitez l'utiliser pour un ensemble avec un nombre impair d'éléments, vous devez ajouter un 0 à l'ensemble pour le rendre pair.la source
Ceci est similaire à un problème de changement de pièce
la source
Algorithme très efficace utilisant des tables que j'ai écrites en couple c ++ il y a quelques années.
Si vous définissez PRINT 1, il imprimera toutes les combinaisons (mais il ne sera pas utilisé la méthode efficace).
Son si efficace qu'il calcule plus de 10 ^ 14 combinaisons en moins de 10 ms.
la source
Version Excel VBA ci-dessous. J'avais besoin de l'implémenter dans VBA (pas ma préférence, ne me jugez pas!), Et j'ai utilisé les réponses sur cette page pour l'approche. Je télécharge au cas où d'autres auraient également besoin d'une version VBA.
La sortie (écrite dans la fenêtre Exécution) doit être:
la source
Voici une meilleure version avec un meilleur formatage de sortie et des fonctionnalités C ++ 11:
la source
Version Java non récursive qui continue simplement d'ajouter des éléments et de les redistribuer parmi les valeurs possibles.
0
sont ignorés et fonctionne pour des listes fixes (ce qui vous est donné est ce que vous pouvez jouer avec) ou une liste de nombres répétables.Exemple d'entrée:
Exemple de sortie:
la source
Pour trouver les combinaisons en utilisant Excel - (c'est assez facile). (Votre ordinateur ne doit pas être trop lent)
Téléchargez le fichier Excel "Sum to Target".
Suivez les instructions sur la page du site Web.
J'espère que cela t'aides.
la source
Conversion Swift 3 de la solution Java: (par @JeremyThompson)
usage:
la source
Cela peut également être utilisé pour imprimer toutes les réponses
La complexité temporelle est exponentielle. Ordre de 2 ^ n
la source
Je faisais quelque chose de similaire pour une mission de scala. Pensé à poster ma solution ici:
la source
J'ai porté l'exemple C # sur Objective-c et je ne l'ai pas vu dans les réponses:
la source
@ Réponse de KeithBeller avec des noms de variables légèrement modifiés et quelques commentaires.
la source
Version PHP , inspirée de la version C # de Keith Beller.
La version PHP de bala ne fonctionnait pas pour moi, car je n'avais pas besoin de grouper les numéros. Je voulais une implémentation plus simple avec une valeur cible et un pool de nombres. Cette fonction supprimera également toutes les entrées en double.
la source
Recommandé comme réponse:
Voici une solution utilisant des générateurs es2015 :
L'utilisation de générateurs peut en fait être très utile car elle vous permet de suspendre l'exécution du script immédiatement après avoir trouvé un sous-ensemble valide. Ceci contraste avec les solutions sans générateurs (c.-à-d. Manquant d’état) qui doivent parcourir tous les sous-ensembles de
numbers
la source
Déduisez 0 en premier lieu. Le zéro est une identité pour l'addition, il est donc inutile par les lois monoïdes dans ce cas particulier. Déduisez également des nombres négatifs si vous voulez grimper jusqu'à un nombre positif. Sinon, vous auriez également besoin d'une opération de soustraction.
Donc ... l'algorithme le plus rapide que vous pouvez obtenir pour ce travail particulier est le suivant donné dans JS.
Il s'agit d'un algorithme très rapide, mais si vous triez le
data
tableau en ordre décroissant, il sera encore plus rapide. L'utilisation.sort()
est insignifiante car l'algorithme se terminera par des invocations beaucoup moins récursives.la source
Version Perl (de la réponse principale):
Résultat:
Version Javascript:
Javascript one-liner qui retourne réellement les résultats (au lieu de l'imprimer):
Et mon préféré, une ligne avec rappel:
la source
Il s'agit d'une solution dynamique pour JS permettant de savoir de combien de façons n'importe qui peut obtenir une certaine somme. Cela peut être la bonne solution si vous pensez à la complexité du temps et de l'espace.
la source
la source
Je n'ai pas aimé la solution Javascript. J'ai vu pourquoi j'en construisais une sur myselft en utilisant des applications partielles, des fermetures et des récursions:
Ok, je me demandais principalement si le tableau de combinaisons pouvait satisfaire la cible requise, mais avec cette approche, vous pouvez commencer à trouver le reste des combinaisons
Ici, définissez simplement la cible et passez le tableau des combinaisons.
la mise en œuvre actuelle, je suis venu avec
la source