Positive und negative Zahlen aus Liste separat addieren

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
lackschuh
User
Beiträge: 281
Registriert: Dienstag 8. Mai 2012, 13:40

Hallo

Was ist der einfachste Weg (so wenig Code wie möglich), damit ich aus einer Liste, welche mit positiven sowie negativen Werten gefüllt ist, die Summer aller positiven Zahlen und analog negative Zahlen anzeigen lassen kann?

Bsp:

Code: Alles auswählen

kassa = [150.00,-35.30,50.00,-5.50]
Ergebnis: 200.0 -40.8

Mit einer for-Schleife und zwei if-Anweisungen bekomm ich es gebacken, allerdings ist der Code dann 8 Zeilen lang...

Für Tipps bedank ich mich schon im Voraus 8)
Zuletzt geändert von lackschuh am Mittwoch 6. Februar 2013, 15:50, insgesamt 1-mal geändert.
BlackJack

@lackschuh: Wofür braucht man da *zwei* ``if``-Anweisungen?
Benutzeravatar
bwbg
User
Beiträge: 407
Registriert: Mittwoch 23. Januar 2008, 13:35

Code: Alles auswählen

sum(filter(lambda e: e > 0, kassa)), sum(filter(lambda e: e < 0, kassa)) # [1][2]
Nicht hübsch, liefert aber das gewünschte Ergebnis.

Grüße ... bwbg

[1] http://docs.python.org/3.3/library/functions.html#sum
[2] http://docs.python.org/3.3/library/func ... tml#filter
"Du bist der Messias! Und ich muss es wissen, denn ich bin schon einigen gefolgt!"
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

Code: Alles auswählen

>>> sum(i for i in kassa if i<0)
-40.799999999999997
>>> sum(i for i in kassa if i>0)
200.0
mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Wenn man schon eine funktionale Lösung nutzt, sollte die zumindest in O(n) ablaufen.

Code: Alles auswählen

reduce(
    lambda (pos, neg), next: (pos + next, neg) if next >= 0 else (pos, neg + next),
    kassa,
    (0, 0)
)
lackschuh
User
Beiträge: 281
Registriert: Dienstag 8. Mai 2012, 13:40

Tausend Dank

@BlackJack
Ich meinte if-else. Sry für das falsche Ausdrücken 8)
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

DasIch hat geschrieben:Wenn man schon eine funktionale Lösung nutzt, sollte die zumindest in O(n) ablaufen.
Du meinst so wie bisher alle genannten Lösungsvorschläge?
Das Leben ist wie ein Tennisball.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

EyDu hat geschrieben:
DasIch hat geschrieben:Wenn man schon eine funktionale Lösung nutzt, sollte die zumindest in O(n) ablaufen.
Du meinst so wie bisher alle genannten Lösungsvorschläge?
Die vorher genannten Vorschläge durchlaufen die Liste der Zahlen zweimal. Der Code von DasIch durchläuft sie nur einmal.

Ob das nicht wieder durch den Overhead an (Lambda-)Funktionsaufrufen aufgefressen wird, ist natürlich eine andere Sache...

Vielleicht zu dem Thema mal ein paar Infos: http://www.python.org/doc/essays/list2str/ (ich weiß aber nicht, wie aktuell die sind)
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

snafu hat geschrieben:Die vorher genannten Vorschläge durchlaufen die Liste der Zahlen zweimal. Der Code von DasIch durchläuft sie nur einmal.
Nacheinander, macht O(2n) und 2n ∈ O(n).
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

Nachdem ich keine Ahnung habe, von was ihr da redet, habe ich das jetzt mal ausprobiert:

Code: Alles auswählen

import random
import time

kassa = range(100000) + range(-100000, 0)
random.shuffle(kassa)

def with_filter():
    t = time.time()
    sum(filter(lambda e: e > 0, kassa))
    sum(filter(lambda e: e < 0, kassa))
    return time.time() - t

def with_lc():
    t = time.time()
    sum(i for i in kassa if i<0)
    sum(i for i in kassa if i>0)
    return time.time() - t

def with_reduce():
    t = time.time()
    reduce(
        lambda (pos, neg), next: (pos + next, neg) if next >= 0 else (pos, neg + next),
        kassa,
        (0, 0)
    )
    return time.time() - t

Code: Alles auswählen

>>> with_filter()
0.10522699356079102
>>> with_lc()
0.066878795623779297
>>> with_reduce()
0.081995010375976562
mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
derdon
User
Beiträge: 1316
Registriert: Freitag 24. Oktober 2008, 14:32

Man muss übrigens nicht die O-Notation kennen, um das zu verstehen. Es reicht aus, wenn man weiß, dass filter unabhängig von den Parametern über die komplette Liste iterieren muss und sum ebenfalls immer über die komplette Liste iteriert. Wenn man das im Hinterkopf hat und das dann mit Lösungen vergleicht, die nur einmal über eine Liste iterieren, sollte einem auffallen, was davon wohl effizienter sein wird. Und das geht ohne Wissen über die Schreibweise O(n), O(2n) etc.
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

Wenn man nur einmal über die Liste iteriert, hat das natürlich den Vorteil, dass man die Liste durch beliebige Iteratoren ersetzten kann.

Code: Alles auswählen

reduce(lambda (pos,neg),a: (pos+(a>0 and a),neg+(a<0 and a)), kassa, (0,0));
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

@derdon
Dass 2 mal iterieren mehr als 1 mal iterieren bedeutet ist mir wohl klar. Allerdings verstehe ich das Ergebnis dann nicht:
Dass die LC-Lösung schneller als die filter-Lösung ist, obwohl beide 2 x über die Liste gehen, liegt IMHO am zusätzlichen Funktionsaufruf den man sich bei der LC spart.
Dass allerdings die Lösung mit 'reduce()' trotz einmaliger Iteration nicht wesentlich schneller als LC ist, schaut für mich danach aus, dass nicht das einmalige oder zweimalige Iterieren, sondern eben der Funktionsaufruf entscheidend ist.

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

@mutella Das liegt allerdings an der Implementation, PyPy liefert Ergebnisse die wesentlich näher sind an dem was man erwarten würde.

Code: Alles auswählen

% pypy
Python 2.7.2 (341e1e3821ff, Jun 07 2012, 15:42:54)
[PyPy 1.9.0 with GCC 4.2.1] on darwin
Type "help", "copyright", "credits" or "license" for more information.
And now for something completely different: ``it's a complete hack, but a very
minimal one (arigato)''
>>>> from foo import *
>>>> with_filter()
0.05291604995727539
>>>> with_lc()
0.05460405349731445
>>>> with_reduce()
0.02114582061767578
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

Die naheliegendste (und langsamste) Lösung will wohl sonst keiner präsentieren. :mrgreen:

Code: Alles auswählen

>>> kassa = [150.00,-35.30,50.00,-5.50]
>>> print map(sum, zip(*((0, item) if item < 0 else (item, 0) for item in kassa)))
[200.0, -40.8]
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

DasIch hat geschrieben:PyPy liefert Ergebnisse die wesentlich näher sind an dem was man erwarten würde.
Die Frage ist, was würde man erwarten?

Wie viel Zeit kostet
1. das Vergleichen zweier Zahlen
2. das Durchlaufen einer Liste
3. das Erstellen von Tupeln
4. das Aufrufen einer Filter-Funktion

Ich hab mal nach Kosten sortiert, wobei ein Funktionsaufruf am teuersten ist.
Damit ist die with_filter-Methode weit abgeschlagen, da 1,2 und 4 jeweils zweifach
ausgeführt wird.
Stellt sich noch die Frage ist 1+2+3 oder 2*(1+2) größer?

Das pypy-Ergebnis verwundert mich.

Code: Alles auswählen

  mov ecx, count
  mov esi, offset kassa
  xor ebx,ebx
  xor edx,edx
@loop:
  lodsd
  or eax,eax
  jns @p
  add ebx,eax
  jmp @g
@p:
  add edx,eax
@g:
  loop @loop
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

Warum unbedingt ein Einzeiler? Was die Anzahl der Zeichen betrifft,
Geschwindigkeit, usw:

Code: Alles auswählen

result = [0, 0]
for n in kassa:
    result[n<0] += n
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Sirius3 hat geschrieben:Warum unbedingt ein Einzeiler? Was die Anzahl der Zeichen betrifft,
Geschwindigkeit, usw:

Code: Alles auswählen

result = [0, 0]
for n in kassa:
    result[n<0] += n
Schon witzig, dass die Lösung ohne den ganzen "funktionalen Kram" die beste Performance zeigt. Zudem ist sie relativ kurz, wirkt elegant und auch nicht zu komplex. Damit ist sie für mich persönlich der Sieger. :)
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

Sirius3 hat geschrieben:

Code: Alles auswählen

result = [0, 0]
for n in kassa:
    result[n<0] += n
Das kann ja jeder. :D

Für mich ist es aber definitiv die übersichtlichste Lösung.
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

Sirius3 hat geschrieben:

Code: Alles auswählen

result = [0, 0]
for n in kassa:
    result[n<0] += n
Echt super schön! Da hat man gleich am morgen ein Lächeln im Gesicht... :)

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Antworten