Ist so etwas effizient ?

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.
Antworten
Benutzeravatar
Goswin
User
Beiträge: 363
Registriert: Freitag 8. Dezember 2006, 11:47
Wohnort: Ulm-Böfingen
Kontaktdaten:

Die folgende Funktion cutoff entfernt überflüssige Nullen am Ende einer Zahlenfolge und funktioniert auch richtig; nur werde ich den Eindruck nicht los, dass ich bei so vielen Umwandlungen und Funktionsaufrufen für so eine einfache Sache mit Kanonen auf Spatzen schieße.

Geht es wirklich nicht einfacher?

Code: Alles auswählen

from itertools import dropwhile

def cutoff(tpl):
   return tuple(reversed(
      tuple(dropwhile( lambda e:not e, reversed(tpl) ))
      ))

folge = (1,2,2,0,0,0)
print(cutoff(folge))  #(1,2,2)
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

Das zweimalige elementweise Erstellen von Tupeln sieht ziemlich ineffizient aus.
Warum suchst Du nicht den Index des Elements, das ≠0 ist, und nutzt slicing.

Code: Alles auswählen

def cutoff(tpl):
    for i, y in enumerate(reversed(tpl)):
        if y:
            return tpl[:-i]
    return ()
Benutzeravatar
Goswin
User
Beiträge: 363
Registriert: Freitag 8. Dezember 2006, 11:47
Wohnort: Ulm-Böfingen
Kontaktdaten:

@Sirius3:

Dein Vorschlag funktioniert nur, wenn auch wirklich überflüssige Nullen vorhanden sind. Wenn keine da sind, zum Beispiel bei tpl=(1,2,2) oder bei einem mehrmaligen Aufruf

Code: Alles auswählen

tpl = cutoff(tpl)
...
tpl = cutoff(tpl)
dann wird die leere Folge () zurückgegeben, was natürlich voll unerwünscht ist.

Man kann das natürlich wie folgt reparieren, aber sehr elegant sieht das dann nicht aus:

Code: Alles auswählen

def cutoff(tpl):
    tpl = tuple(tpl) + (0,)
    for i, y in enumerate(reversed(tpl)):
        if y:
            return tpl[:-i]
    return ()
Benutzeravatar
bwbg
User
Beiträge: 407
Registriert: Mittwoch 23. Januar 2008, 13:35

Sirius' Funktion leicht abgewandelt:

Code: Alles auswählen

def cutoff(seq):
    for i, y in enumerate(reversed(seq)):
        if y: break
    return seq[0:len(seq) - i]
Grüße ... bwbg
"Du bist der Messias! Und ich muss es wissen, denn ich bin schon einigen gefolgt!"
BlackJack

@bwbg: Das dürfte dafür jetzt Probleme mit einer leeren Sequenz haben, weil dann `i` nicht definiert ist. :-)
Benutzeravatar
bwbg
User
Beiträge: 407
Registriert: Mittwoch 23. Januar 2008, 13:35

Mein Anwalt sagt, dass eine leere Sequenz nie Teil der Anforderung des OP gewesen ist. :mrgreen:

Ganz kostenlos und nicht ganz ernst gemeint liefere ich folgendes hinterher:

Code: Alles auswählen

def cutoff_for_almost_every_corner_case_you_may_think_about(seq):
    try:
        return cutoff(seq)
    except UnboundLocalError:
        return ()
Ich hoffe, das Internet wird irgendwann mal gelöscht. :roll:

Grüße ... bwbg
"Du bist der Messias! Und ich muss es wissen, denn ich bin schon einigen gefolgt!"
xeike
User
Beiträge: 83
Registriert: Donnerstag 28. Februar 2013, 09:58

Hier eine Lösung ohne reverse:

Code: Alles auswählen

while folge[-1] == 0:
    folge = folge[:-1]
Falls du auch noch leere Folgen brauchst, dann ist

Code: Alles auswählen

while folge and folge[-1] == 0:
ganz gut :lol:
BlackJack

@xeike: Effizienzpreise gewinnt man damit aber nicht wenn für jedes Element was abgeschnitten wird, die Sequenz kopiert werden muss.
Benutzeravatar
bwbg
User
Beiträge: 407
Registriert: Mittwoch 23. Januar 2008, 13:35

Da stellt sich die Frage nach der (Ausführungs-)Effizienz aber nicht mehr. Beim Slicing wird eine neue Sequenz erzeugt (sprich das passende kopiert) und dies geschieht in Deinem Beispiel in jedem Schleifendurchlauf.

Grüße ... bwbg

Edit: Zu spät. And now you have to say: "Ah, interesting"
"Du bist der Messias! Und ich muss es wissen, denn ich bin schon einigen gefolgt!"
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Ach, eine lineares Problem in quadratischer Laufzeit zu lösen ist doch quasi optimal ;-)

@bwbg: Gibs doch zu: Du hast die nicht mit deinem Anwalt unterhalten, du wolltest nur nicht zugeben, dass du das i=0 vergessen hast:

Code: Alles auswählen

def cutoff(seq):
    i = 0
    for i, y in enumerate(reversed(seq)):
        if y: break
    return seq[0:len(seq) - i]
Das Leben ist wie ein Tennisball.
xeike
User
Beiträge: 83
Registriert: Donnerstag 28. Februar 2013, 09:58

Ach so, wenn es um Effizienz geht... :)

Code: Alles auswählen

def dingsbums(foo):
    i = 1
    while foo[-i] == 0:
        i = i + 1
    return foo[0:len(foo) - i + 1]

l = [1] + [0 for i in range(10000)]
timeit.default_timer() ; dingsbums(l) ; timeit.default_timer()
 
So, für den Rest ist es mir jetzt zu spät.

Xe

PS: Für wenig abzuschneidendes macht "dingsbums" das Rennen, für viel hingegen "cutoff".
Benutzeravatar
Goswin
User
Beiträge: 363
Registriert: Freitag 8. Dezember 2006, 11:47
Wohnort: Ulm-Böfingen
Kontaktdaten:

:D Vielen Dank für alle eure Lösungsvorschläge. Es erstaunt mich immer wieder, wie oft man (oder zumindest ich) die einfachen Dinge übersehen kann.
Antworten