Algoritmul pentru a afla ce număr într-o listă rezuma la un anumit număr

voturi
20

Am o listă de numere. Am, de asemenea, o anumită sumă. Suma se face din câteva numere din lista mea (I / nu știu câte numere este făcută din). Există un algoritm rapid pentru a obține o listă de numere posibile? Scrisă în Python ar fi mare, dar pseudo-cod este prea bun. (Nu pot citi încă nimic altceva decât Python: P)

Exemplu

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

NOTĂ: Eu știu de algoritm pentru a afla ce numere dintr - o listă de dimensiune n sumă la un alt număr . (Dar eu nu pot citi C # și sunt în imposibilitatea de a verifica dacă funcționează pentru nevoile mele Sunt pe Linux și am încercat , folosind Mono dar nu primesc erori și nu pot să dau seama cum să lucreze C # :(
și eu știu de algoritm pentru a rezuma o listă de numere pentru toate combinațiile (dar se pare a fi destul de ineficientă. nu am nevoie de toate combinațiile .)

Întrebat 06/08/2010 la 05:09
sursa de către utilizator
În alte limbi...                            


4 răspunsuri

voturi
33

Această problemă se reduce la 0-1 Raniță Problema , în cazul în care încercați să găsească un set cu o sumă exactă. Soluția depinde de constrângerile, în cazul general , această problemă este NP-completă.

Cu toate acestea, în cazul în care suma maximă de căutare (să spunem S) nu este prea mare, atunci puteți rezolva problema cu ajutorul programării dinamice. Voi explica folosind o funcție recursivă și memoization , care este mai ușor de înțeles decât o abordare de jos în sus.

Să cod o funcție f(v, i, S), astfel încât acesta returnează numărul de subseturi în v[i:]care însumează exact S. Pentru a rezolva recursiv, mai întâi trebuie să analizăm baza ( de exemplu: v[i:]este gol):

  • S == 0: Singurul subset de []are suma 0, deci este un subset valid. Din acest motiv, funcția ar trebui să se întoarcă 1.

  • S = 0:! Ca doar subsetul de []are suma 0, nu există un subset valid. Din acest motiv, funcția ar trebui să se întoarcă 0.

Apoi, să analizăm cazul recursiv ( de exemplu: v[i:]nu este gol). Există două opțiuni: includ numărul v[i]în subsetul curent, sau nu - l includ. Dacă includem v[i], atunci suntem în căutarea subseturi care au sume S - v[i], în caz contrar, suntem încă în căutarea pentru subseturi cu suma S. Funcția fpoate fi implementată în felul următor:

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

Prin verificarea f(v, 0, S) > 0, puteți ști dacă există o soluție la problema ta. Cu toate acestea, acest cod este prea lent, fiecare apel recursiv icrelor două noi apeluri, ceea ce duce la o O (2 ^ n) algoritm. Acum, putem aplica memoization pentru a face rula în timp O (n * S), care este mai rapid dacă Snu este prea mare:

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

Acum, este posibil să codul o funcție gcare returnează un subset care însumează S. Pentru a face acest lucru, este suficient pentru a adăuga elemente numai în cazul în care există cel puțin o soluție de a le include:

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

Disclaimer: Această soluție spune că există două subseturi de [10, 10], care însumează 10. Acest lucru se datorează faptului că se presupune că primele zece este diferită de cea de a doua zece. Algoritmul poate fi fixat să se presupună că ambele zeci sunt egale (și, astfel, să răspundă unul), dar că este un pic mai complicat.

Publicat 06/08/2010 la 06:16
sursa de către utilizator

voturi
1

Deci, logica este de a inversa un fel numerele, și să presupunem lista numerelor este l și suma care urmează să fie format este s .

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

Apoi, vom trece prin această buclă și un număr este selectat dintre l în ordine și să zicem că este eu . există 2 cazuri posibile , fie i este parte a sumei sau nu. Deci, presupunem că am face parte din soluție și atunci problema se reduce la l ființei l[l.index(i+1):]și e ființa si asa, daca functia noastra este (l, e) atunci numim noi a(l[l.index(i+1):] ,s-i). și dacă eu nu este o parte a lui , atunci avem de a forma e din l[l.index(i+1):]listă. Deci, este similară în ambele cazuri, numai schimbarea este dacă am face parte din s, numai atunci s = Si si altfel s = s.

acum pentru a reduce problema, astfel încât într-un număr de caz în l sunt mai mari decât s le eliminăm pentru a reduce complexitatea până nu este gol și în acest caz, numerele care sunt selectate nu sunt o parte a soluției noastre și ne întoarcem false.

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

iar în cazul în care L are doar un element de stânga, apoi, fie poate fi o parte a lui, atunci ne întoarcem adevărat sau nu, atunci vom reveni fals și bucla va trece prin alt număr.

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

nota în buclă în cazul în care au folosit b..but b este lista noastră de only.and am rotunjit ori de câte ori este posibil, astfel încât să nu ar trebui sa răspuns greșit din cauza calcule în virgulă mobilă în 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)

această soluție funcționează fast.more rapidă decât cea explicată mai sus. Totuși, acest lucru funcționează doar pentru numere pozitive. Cu toate acestea, de asemenea, funcționează bine în cazul în care există o soluție doar în caz contrar este nevoie de prea mult timp pentru a iesi din bucle.

un exemplu rula este ca acest lucru vă permite să spun

    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

doar pentru a da o comparație pe care am fugit de pe calculatorul meu, care nu este atât de bun. utilizând

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]

și

s = 2000

bucla mea a fugit 1018 de ori și 31 ms.

și bucla de cod anterior a fugit 3415587 ori și a luat undeva aproape de 16 secunde.

Cu toate acestea, în cazul în care o soluție nu există codul meu a fugit mai mult de câteva minute, așa că am oprit și codul anterior a fugit doar aproape în jurul valorii de 17 ms și codul anterior funcționează cu numere negative, de asemenea.

așa că am unele îmbunătățiri lucru se poate face.

Publicat 24/12/2015 la 19:29
sursa de către utilizator

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

Acest cod Python face ceea ce ai cerut, se va imprima o pereche unică de numere a căror sumă este egală cu variabila țintă.

dacă numărul țintă este de 8, se va imprima: 
1 7
2 6
3 5
3 5
5 3
6 2
9 -1
5 3
Publicat 08/02/2017 la 04:38
sursa de către utilizator

voturi
0

Am găsit un răspuns care nu mai are timp de complexitate O (n) și complexitatea spațiului despre O (2n), unde n este lungimea listei.

Răspunsul îndeplinește următoarele constrângeri:

  1. Lista poate conține duplicate, de exemplu, [1,1,1,2,3] și doriți să găsiți perechile însumează 2

  2. Lista poate conține atât numere întregi pozitive și negative

Codul este la fel de mai jos, și urmat de explicația:

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. Creați un dicționar gol, itera prin listă și a pus toate cheile posibile în dict cu valoarea inițială 0. Rețineți că este necesară cheia (k-ITER1) pentru a specifica, de exemplu, în cazul în care lista conține 1, dar nu conține 4, și suma este 5. atunci când ne uităm la 1, ne-ar dori să găsească câte 4 avem, dar dacă 4 nu este în dict, atunci se va ridica o eroare.
  2. Itera lista din nou, și numărul de câte ori apare fiecare număr întreg și stoca rezultatele la dict.
  3. Itera prin dict, de data aceasta este de a găsi cât de multe perechi avem. Trebuie să ia în considerare 3 condiții:

    3.1 Cheia este doar jumătate din suma și această cheie apare mai mult decât o dată pe listă, de exemplu, lista este [1,1,1], suma este 2. Noi tratăm această condiție specială ca ceea ce face codul.

    3.2 Cheia este doar jumătate din suma și această cheie are loc o singură dată în listă, vom sări peste această condiție.

    3.3 Pentru alte cazuri care cheia nu este de jumătate din suma, doar multiplica valoarea sa cu valoarea o altă cheie în cazul în care aceste două chei suma la valoarea dată. De exemplu, dacă suma este de 6, înmulțim temp [1] și temp [5], temp [2] și temp [4], etc ... (nu am lista de cazuri în care numerele sunt negative, dar ideea este aceeași. )

Pasul cel mai complex este pasul 3, care presupune căutarea dicționarului, dar căutarea dicționarului este de obicei rapid complexitate, aproape constantă. (Deși cel mai rău caz, este O (n), dar nu ar trebui să se întâmple pentru chei intregi.) Astfel, presupunând cu căutarea este complexitatea constantă, complexitatea totală este O (n), așa cum am itera numai lista de mai multe ori separat.

Sfaturi pentru o soluție mai bună este binevenită :)

Publicat 08/10/2017 la 09:30
sursa de către utilizator

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