2017-01-28 1 views
0

Ich muss eine Funktion schreiben, die die Anzahl der Möglichkeiten zum Erreichen einer bestimmten Anzahl durch Hinzufügen von Nummern einer Liste zurückgibt. Zum Beispiel:eine Funktion, die Anzahl der Summen einer bestimmten Zahl zurückgibt.py

print(p([3,5,8,9,11,12,20], 20)) 
should return:5 

Der Code, den ich geschrieben habe, ist:

def pow(lis): 
    power = [[]] 
    for lst in lis: 
     for po in power: 
      power = power + [list(po)+[lst]] 
    return power 

def p(lst, n): 
    counter1 = 0 
    counter2 = 0 
    power_list = pow(lst) 
    print(power_list) 
    for p in power_list: 
     for j in p: 
      counter1 += j 
     if counter1 == n: 
      counter2 += 1 
      counter1 == 0 
     else: 
      counter1 == 0 
    return counter2 

pow() ist eine Funktion, die alle von den Untergruppen der Liste und p gibt sollte die Anzahl der Möglichkeiten gibt die Anzahl n zu erreichen. Ich bekomme immer eine Ausgabe von Null und ich verstehe nicht warum. Ich würde gerne Ihre Eingabe dafür hören. Vielen Dank im Voraus.

+2

Da "pow" eine integrierte Funktion ist, möchten Sie vielleicht einen anderen Namen wählen. In jedem Fall, wenn alle Zahlen als positiv bekannt sind, ist das Überfahren aller Teilmengen übertrieben. –

+2

Auch - wenn dies keine Hausaufgaben sind und von Ihnen erwartet wird, alles von Grund auf neu zu machen, ist die Verwendung des 'itertools'-Moduls der einfachste Weg, Teilmengen zu zählen. –

+2

'counter1 == 0' sollte' counter1 = 0' sein (zweimal). Schließen für Tippfehler. Kann auch vereinfacht werden zu 'counter2 = sum (sum (p) == n für p in power_list)' –

Antwort

1

Eine One-Pass-Lösung mit einem Zähler, die Zusätze minimieren.

def one_pass_sum(L,target): 
    sums = [0] 
    cnt = 0 
    for x in L: 
     for y in sums[:]: 
      z = x+y 
      if z <= target : 
       sums.append(z) 
       if z == target : cnt += 1 
    return cnt 

diese Weise, wenn n=len(L), machen Sie weniger als 2^n Ergänzungen gegen n/2 * 2^n durch alle Summen berechnen.

EDIT:

Eine effizientere Lösung, zählt, dass nur Wege. Die Idee ist zu sehen, dass, wenn es k Möglichkeiten gibt, z-x zu machen, gibt es k mehr Weg, z zu tun, wenn x entstehen.

def enhanced_sum_with_lists(L,target): 
    cnt=[1]+[0]*target # 1 way to make 0 
    for x in L: 
     for z in range(target,x-1,-1): # [target, ..., x+1, x] 
      cnt[z] += cnt[z-x] 
    return cnt[target] 

Aber Reihenfolge ist wichtig: z muss Nachkomme hier in Betracht gezogen werden, die guten Zählungen (Dank PM 2Ring) zu haben.

Dies kann sehr schnell sein (n*target Ergänzungen) für große Listen.

Zum Beispiel:

>>> enhanced_sum_with_lists(range(1,100),2500) 
875274644371694133420180815 

wird in 61 ms erhalten. Es wird das Zeitalter des Universums brauchen, um es mit der ersten Methode zu berechnen.

+0

Wenn Sie nur das Ergebnis für das Ziel möchten, geben Sie cnt [Ziel] zurück. Ich bearbeite den Beitrag für Klarheit. –

+0

Vielen Dank für das Erkennen dieses Fehlers, ich korrigiere und versuche es zu erklären. Es ist etwa 400x schneller als ein Listenansatz für '* args = range (1,41), 70', weil nur wenige Summen berechnet werden. –

+0

Gute Arbeit! Ich würde Ihnen eine weitere Aufwertung geben, wenn ich könnte. :) BTW, Ihr 'sum_with_dict' ist ungefähr 6 Mal schneller als mein' subset_sums' mit 'Bereich (1,41), 70' auf Python 3.6.0 auf meinem alten 32-Bit Single-Core-Rechner. –

1

Es gibt zwei Tippfehler in Ihrem Code: counter1 == 0 ist ein Boolean, es setzt nichts zurück.

sollte diese Version arbeiten:

def p(lst, n): 
    counter2 = 0 
    power_list = pow(lst)  
    for p in power_list: 
     counter1 = 0 #reset the counter for every new subset 
     for j in p: 
      counter1 += j 
     if counter1 == n: 
      counter2 += 1 
    return counter2 
+0

"Sie setzen Counter1 nur zurück, wenn Sie insgesamt n haben" Dies ist nicht wahr; er setzt 'counter1' in beiden Fällen zurück, im 'if' und im 'else' - oder er würde es tun, wenn es nicht den einfachen Tippfehler gäbe. –

+0

Oh, absolut, ich habe es versäumt, den ersten Tippfehler ^^ – Faibbus

0
from itertools import chain, combinations 

def powerset_generator(i): 
    for subset in chain.from_iterable(combinations(i, r) for r in range(len(i)+1)): 
     yield set(subset) 

def count_sum(s, cnt): 
    return sum(1 for i in powerset_generator(s) if sum(k for k in i) == cnt) 

print(count_sum(set([3,5,8,9,11,12,20]), 20)) 
+1

FWIW zu sehen, Code-only-Antworten werden nicht gut auf SO erhalten. Es ist wahrscheinlicher, dass du aufsteigst - Stimmen (und wahrscheinlich weniger Stimmen), wenn du Text hinzufügst, der deine Antwort erklärt. –

1

Als tobias_k und Faibbus erwähnt, haben Sie einen Tippfehler: counter1 == 0 statt counter1 = 0, an zwei Stellen. Der counter1 == 0 erzeugt ein boolesches Objekt von True oder False, aber da Sie das Ergebnis dieses Ausdrucks nicht zuweisen, wird das Ergebnis verworfen. Es wird kein SyntaxError ausgelöst, da ein Ausdruck, der nicht zugewiesen wird, legales Python ist.

Wie John Coleman und B. M. erwähnen, ist es nicht effizient, das volle Powerset zu erstellen und dann jede Teilmenge zu testen, um zu sehen, ob sie die richtige Summe hat. Dieser Ansatz ist in Ordnung, wenn die Eingabesequenz klein ist, aber selbst für Sequenzen mittlerer Größe sehr langsam. Wenn Sie tatsächlich eine Liste erstellen, die die Subsets enthält, anstatt einen Generator zu verwenden und die Subsets zu testen, werden sie bald ausgeführt aus dem RAM.

B. M.Die erste Lösung ist ziemlich effizient, da sie keine Teilmengen erzeugt, die größer als die Zielsumme sind. (Ich bin mir nicht sicher, was B. M. mit dieser diktierten Lösung macht ...).

Aber wir können diesen Ansatz verbessern, indem wir die Liste der Summen sortieren. Auf diese Weise können wir aus der inneren for Schleife ausbrechen, sobald wir eine zu hohe Summe entdecken. Richtig, wir müssen die -Liste auf jeder Iteration der äußeren for-Schleife sortieren, aber glücklicherweise ist Pythons TimSort sehr effizient und für die Sortierung einer Liste mit sortierten Untersequenzen optimiert. Daher ist es ideal für diese Anwendung.

def subset_sums(seq, goal): 
    sums = [0] 
    for x in seq: 
     subgoal = goal - x 
     temp = [] 
     for y in sums: 
      if y > subgoal: 
       break 
      temp.append(y + x) 
     sums.extend(temp) 
     sums.sort() 
    return sum(1 for y in sums if y == goal) 

# test 

lst = [3, 5, 8, 9, 11, 12, 20] 
total = 20 
print(subset_sums(lst, total)) 

lst = range(1, 41) 
total = 70 
print(subset_sums(lst, total)) 

Ausgang

5 
28188 

Mit lst = range(1, 41) und total = 70, dieser Code ist etwa 3 mal schneller als die B. M. listet Version auf.

Verwandte Themen