Algorithme pour trouver le numéro dans une liste somme à un certain nombre

voix
20

J'ai une liste de numéros. J'ai aussi une certaine somme. La somme est faite de quelques numéros de ma liste (je / sache pas combien de chiffres, il est fabriqué à partir). Y at-il un algorithme rapide pour obtenir une liste de numéros possibles? Écrit en Python serait génial, mais pseudo-code est bon aussi. (Je ne peux pas lire encore autre chose que Python: P)

Exemple

list = [1,2,3,10]
sum = 12
result = [2,10]

NOTE: Je ne connais algorithme pour trouver des numéros d'une liste de somme taille de n à un autre numéro . (Mais je ne peux pas lire C # et je suis incapable de vérifier si cela fonctionne pour mes besoins , je suis sous Linux et j'ai essayé d' utiliser mono mais je reçois des erreurs et je ne peux pas comprendre comment travailler C # :(
et je ne sais d' algorithme pour résumer la liste des numéros pour toutes les combinaisons (mais il semble être assez inefficace. Je ne ai pas besoin de toutes les combinaisons .)

Créé 06/08/2010 à 05:09
source utilisateur
Dans d'autres langues...                            


4 réponses

voix
33

Ce problème se réduit au 0-1 Knapsack problème , vous essayez de trouver un ensemble avec une somme exacte. La solution dépend des contraintes, dans le cas général , ce problème est NP-complet.

Toutefois, si la somme maximale de recherche (appelons - le S) ne soit pas trop élevé, alors vous pouvez résoudre le problème en utilisant la programmation dynamique. Je vais vous expliquer à l'aide d' une fonction récursive et memoization , ce qui est plus facile à comprendre qu'une approche ascendante.

Soit son code une fonction f(v, i, S), telle qu'elle retourne le nombre de sous - ensembles en v[i:]qui résume exactement S. Pour résoudre récursive, nous devons d'abord analyser la base (ex: v[i:]est vide):

  • S == 0: Le seul sous - ensemble de []a 0 somme, il est donc un sous - ensemble valide. À cause de cela, la fonction doit retourner 1.

  • S = 0: Comme le seul sous - ensemble de []a somme 0, il n'y a pas un sous - ensemble valide. À cause de cela, la fonction doit retourner 0.

Ensuite, nous allons analyser le cas récursive (ie: v[i:]n'est pas vide). Il y a deux choix: comprennent le nombre v[i]dans le sous - ensemble actuel, ou comprennent pas. Si l' on ajoute v[i], nous recherchons des sous - ensembles qui ont somme S - v[i], sinon, nous cherchons toujours des sous - ensembles avec somme S. La fonction fpeut être mise en œuvre de la manière suivante:

def f(v, i, S):
  if i >= len(v): return 1 if S == 0 else 0
  count = f(v, i + 1, S)
  count += f(v, i + 1, S - v[i])
  return count

v = [1, 2, 3, 10]
sum = 12
print(f(v, 0, sum))

En cochant f(v, 0, S) > 0, vous pouvez savoir s'il y a une solution à votre problème. Cependant, ce code est trop lent, chaque appel récursif engendre deux nouveaux appels, ce qui conduit à un O (2 ^ n) algorithme. Maintenant, nous pouvons appliquer memoization pour le faire fonctionner en temps O (n * S), qui est plus rapide si Sest pas trop grand:

def f(v, i, S, memo):
  if i >= len(v): return 1 if S == 0 else 0
  if (i, S) not in memo:  # <-- Check if value has not been calculated.
    count = f(v, i + 1, S, memo)
    count += f(v, i + 1, S - v[i], memo)
    memo[(i, S)] = count  # <-- Memoize calculated result.
  return memo[(i, S)]     # <-- Return memoized value.

v = [1, 2, 3, 10]
sum = 12
memo = dict()
print(f(v, 0, sum, memo))

Maintenant, il est possible de coder une fonction gqui retourne un sous - ensemble qui résume S. Pour ce faire, il suffit d'ajouter des éléments que s'il y a au moins une solution en les incluant:

def f(v, i, S, memo):
  # ... same as before ...

def g(v, S, memo):
  subset = []
  for i, x in enumerate(v):
    # Check if there is still a solution if we include v[i]
    if f(v, i + 1, S - x, memo) > 0:
      subset.append(x)
      S -= x
  return subset

v = [1, 2, 3, 10]
sum = 12
memo = dict()
if f(v, 0, sum, memo) == 0: print("There are no valid subsets.")
else: print(g(v, sum, memo))

Avertissement: Cette solution dit qu'il ya deux sous-ensembles de [10, 10] qui résume 10. En effet, il suppose que les dix premiers est différente de la seconde dix. L'algorithme peut être fixé à supposer que les deux dizaines sont égaux (et donc répondre à un), mais qui est un peu plus compliqué.

Créé 06/08/2010 à 06:16
source utilisateur

voix
1

Ainsi, la logique est d'inverser sorte les chiffres, et supposons que la liste des numéros est l et somme à former est s .

   for i in b:
            if(a(round(n-i,2),b[b.index(i)+1:])):
                r.append(i)    
                return True
        return False

puis, nous allons à travers cette boucle et un numéro est sélectionné de l en ordre et laisser dire qu'il est i . il y a 2 cas possibles , soit i est la partie de la somme ou non. Donc, nous supposons que je fait partie de la solution et le problème se réduit à l être l[l.index(i+1):]et de l' être si oui, si notre fonction est un (l, s) nous appelons a(l[l.index(i+1):] ,s-i). et si je ne fait pas partie de s alors nous devons former s de la l[l.index(i+1):]liste. Il est donc similaire dans les deux cas, seul changement est si je fait partie de s, alors s = si et autrement s = s seulement.

maintenant pour réduire le problème tel que le nombre de cas dans l sont supérieurs à s nous les retirons de réduire la complexité jusqu'à ce que l est vide et dans ce cas, les numéros qui sont sélectionnés ne font pas partie de notre solution et nous retournons faux.

if(len(b)==0):
    return False    
while(b[0]>n):
    b.remove(b[0])
    if(len(b)==0):
        return False    

et dans le cas où l n'a que 1 élément gauche alors soit il peut faire partie de s alors nous revenons vrai ou ce n'est pas alors nous revenons faux et boucle passer par un autre numéro.

if(b[0]==n):
    r.append(b[0])
    return True
if(len(b)==1):
    return False

noter dans la boucle si ont utilisé b..but b est notre liste only.and i ont arrondi partout où il est possible, de sorte que nous ne devrions pas avoir de mauvaise réponse en raison de calculs à virgule flottante en python.

r=[]
list_of_numbers=[61.12,13.11,100.12,12.32,200,60.00,145.34,14.22,100.21,14.77,214.35,200.32,65.43,0.49,132.13,143.21,156.34,11.32,12.34,15.67,17.89,21.23,14.21,12,122,134]
list_of_numbers=sorted(list_of_numbers)
list_of_numbers.reverse()
sum_to_be_formed=401.54
def a(n,b):
    global r
    if(len(b)==0):
        return False    
    while(b[0]>n):
        b.remove(b[0])
        if(len(b)==0):
            return False    
    if(b[0]==n):
        r.append(b[0])
        return True
    if(len(b)==1):
        return False
    for i in b:
        if(a(round(n-i,2),b[b.index(i)+1:])):
            r.append(i)    
            return True
    return False
if(a(sum_to_be_formed,list_of_numbers)):
    print(r)

cette solution fonctionne fast.more rapide d'un expliqué ci-dessus. Cependant, cela fonctionne pour les nombres positifs seulement. Cependant aussi fonctionne bien s'il y a une solution que sinon il faut pour beaucoup de temps pour sortir des boucles.

une course exemple est comme ceci permet de dire

    l=[1,6,7,8,10]

and s=22 i.e. s=1+6+7+8
so it goes through like this 

1.) [10, 8, 7, 6, 1] 22
i.e. 10  is selected to be part of 22..so s=22-10=12 and l=l.remove(10)
2.) [8, 7, 6, 1] 12
i.e. 8  is selected to be part of 12..so s=12-8=4 and l=l.remove(8)
3.) [7, 6, 1] 4  
now 7,6 are removed and 1!=4 so it will return false for this execution where 8 is selected.
4.)[6, 1] 5
i.e. 7  is selected to be part of 12..so s=12-7=5 and l=l.remove(7)
now 6 are removed and 1!=5 so it will return false for this execution where 7 is selected.
5.)[1] 6
i.e. 6  is selected to be part of 12..so s=12-6=6 and l=l.remove(6)
now 1!=6 so it will return false for this execution where 6 is selected.
6.)[] 11
i.e. 1 is selected to be part of 12..so s=12-1=1 and l=l.remove(1)
now l is empty so all the cases for which 10 was a part of s are false and so 10 is not a part of s and we now start with 8 and same cases follow.
7.)[7, 6, 1] 14
8.)[6, 1] 7
9.)[1] 1

juste pour donner une comparaison que je courais sur mon ordinateur qui est pas si bon. en utilisant

l=[61.12,13.11,100.12,12.32,200,60.00,145.34,14.22,100.21,14.77,214.35,145.21,123.56,11.90,200.32,65.43,0.49,132.13,143.21,156.34,11.32,12.34,15.67,17.89,21.23,14.21,12,122,134]

et

s = 2000

ma boucle a couru 1018 fois et 31 ms.

et la boucle de code précédent a couru 3415587 fois et a pris quelque part près de 16 secondes.

Cependant, dans le cas où une solution n'existe pas mon code a couru plus de quelques minutes si je me suis arrêté et code précédent a couru près d'environ 17 ms seulement et code précédent fonctionne avec des nombres négatifs aussi.

de sorte que je certaines améliorations peuvent être fait.

Créé 24/12/2015 à 19:29
source utilisateur

voix
0
#!/usr/bin/python2

ylist = [1, 2, 3, 4, 5, 6, 7, 9, 2, 5, 3, -1]
print ylist 
target = int(raw_input("enter the target number")) 
for i in xrange(len(ylist)):
    sno = target-ylist[i]
    for j in xrange(i+1, len(ylist)):
        if ylist[j] == sno:
            print ylist[i], ylist[j]

Ce code python faire ce que vous avez demandé, il imprimera la paire unique de chiffres dont la somme est égale à la variable cible.

si le nombre cible est 8, il imprimera: 
1 7
2 6
3 5
3 5
5 3
6 2
9 -1
5 3
Créé 08/02/2017 à 04:38
source utilisateur

voix
0

J'ai trouvé une réponse qui a la complexité d'exécution O (n) et de la complexité de l'espace sur les O (2n), où n est la longueur de la liste.

La réponse satisfait aux contraintes suivantes:

  1. Liste peut contenir des doublons, par exemple [1,1,1,2,3] et que vous voulez trouver des paires 2 à additionnent

  2. Liste peut contenir des nombres entiers positifs et négatifs

Le code est comme ci-dessous, et suivi de l'explication:

def countPairs(k, a):
    # List a, sum is k
    temp = dict()
    count = 0
    for iter1 in a:
        temp[iter1] = 0
        temp[k-iter1] = 0
    for iter2 in a:
        temp[iter2] += 1
    for iter3 in list(temp.keys()):
        if iter3 == k / 2 and temp[iter3] > 1:
            count += temp[iter3] * (temp[k-iter3] - 1) / 2
        elif iter3 == k / 2 and temp[iter3] <= 1:
            continue
        else:
            count += temp[iter3] * temp[k-iter3] / 2
    return int(count)
  1. Créer un dictionnaire vide, itérer la liste et de mettre toutes les clés possibles dans la dict avec la valeur initiale 0. Notez que la clé (k-iter1) est nécessaire de préciser, par exemple, si la liste contient 1 mais contient 4, et somme est 5. puis, quand nous regardons 1, nous aimerions trouver combien de 4 avons-nous, mais si 4 est pas dans le dict, il déclenche une erreur.
  2. Itérer sur la liste à nouveau, et comptez combien de fois que chaque entier se produit et stocker les résultats à la dict.
  3. Itérer à travers à travers le dict, cette fois-ci est de trouver combien de paires devons-nous. Nous devons considérer 3 conditions:

    3.1 La clé est juste la moitié de la somme et cette clé se produit plus d'une fois dans la liste, la liste est par exemple [1,1,1], somme est 2. Nous traitons cette condition particulière comme ce que fait le code.

    3.2 La clé est juste la moitié de la somme et cette clé se produit qu'une seule fois dans la liste, nous sautons cette condition.

    3.3 Pour les autres cas, cette clé n'est pas la moitié de la somme, il suffit de multiplier la valeur avec sa valeur d'une autre clé où ces deux clés résument à la valeur donnée. Par exemple, si la somme est de 6, on multiplie température [1] et la température [5], la température [2] et la température [4], etc ... (je n'ai pas la liste des cas où les nombres sont négatifs, mais idée est la même. )

L'étape la plus complexe est l'étape 3, qui consiste à rechercher le dictionnaire, mais comme la recherche le dictionnaire est généralement rapide, la complexité presque constante. (Bien que le pire des cas est O (n), mais ne devrait pas se produire pour les clés entières.) Ainsi, avec la recherche est en supposant constante la complexité, la complexité totale est O (n) que nous ne parcourons la liste plusieurs fois séparément.

Conseils pour une meilleure solution est la bienvenue :)

Créé 08/10/2017 à 09:30
source utilisateur

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more