Scheme-Implementationen sind verpflichtet TCO anzubieten um sich Scheme zu nennen, das ist bei C nicht der Fall, also kann man nicht einfach so davon ausgehen dass Endrekursion optimiert wird. Damit ist TCO Teil der Sprache in Scheme aber nicht in C, wo es eben Implementationsabhängig ist.lunar hat geschrieben:Was meinst du denn damit? Moderne C-Compiler sollten Endrekursionen doch zu Iterationen optimieren, oder etwa nicht?Außerdem haben weder C [...] TCO, also stößt man da durchaus auch an ein Limit irgendwann.
Rekursionstiefe feststellen
-
- Python-Forum Veteran
- Beiträge: 16025
- Registriert: Freitag 20. Juni 2003, 16:30
- Kontaktdaten:
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
@lunar: C Compiler können TCO vielleicht machen, es gibt aber von Seiten der Sprachdefinition keinerlei Garantien.
Nein, nein, nein, nein! Ich meinte, dass ICH mir nicht sicher bin, ob das gemacht wird oO' So ein "Ohne Gewähr!"... Will keine Halbwahrheiten erzählen, ohne es 100% zu wissen ._.Leonidas hat geschrieben:Warum sollte TCO unsicher sein? Natürlich kann das nur Endrekursion optimieren, aber das steht ja schon im Namen.BlackVivi hat geschrieben:Selbst Sprachen die stark optimiert sind wie C oder Java sind nicht sonderlich effektiv wenn es um Rekursion geht. (Tails-Recursion mal abgesehen, aber die wird sowieso in eine iterative Variante vom Compiler umgewandelt, oder? Nicht sonderlich sicher)
Außerdem haben weder C noch Java TCO, also stößt man da durchaus auch an ein Limit irgendwann.
Verzeih, undeutlich ausgedrückt!
Das war aber nicht der Fall, dennstr1442 hat geschrieben:BlackJack meint damit (vermutlich ), daß die Funktionen so angelegt sind, das der Aufruf einer bestimmten Funktion (hier der main() Funktion) niemals zurückkehren soll und somit als eine Art "Sprung" dient.
a) main() wurde für jede Belegung von liste aufgerufen
b) es gäbe kein Problem mit dem Zähler, wenn main() nicht zurückkehren würde
c) das Problem würde nicht durch Herabsetzen des Zählers gelöst werden können
Der Name main war IMO einfach nur sehr unkonventionell (und daher schlecht).
Man kann die Originalversion z.B. so aufrufen:BlackJack hat geschrieben:@bords0: Was fehlt jetzt noch bei meiner Version?
Code: Alles auswählen
abc(3)
Zu lesen finde ich es aber schwieriger. Sind die -1en immer richtig? Passen reverse, Schleifenanzahl und Indizierung zusammen?BlackJack hat geschrieben:Und letztendlich fand ich es schon relativ trivial die Funktionalität iterativ zu schreiben.
Allerdings! (Unabhängig von rekursiv/iterativ).BlackJack hat geschrieben:Äusserst unglücklich finde ich übrigens auch noch dass auf eine globale Liste zugegriffen wird. Für sauberen, verständlichen Code sollte man das auch vermeiden.
Vielleicht lernt der OP bei der ganzen Diskussion ja wenigstens itertools besser kennen, dann hat sich der Thread doch noch gelohntBlackJack hat geschrieben:Und falls man Python >=2.6 einsetzt, sollte man sich sowieso überlegen, ob man `itertools.combinations()` umbedingt selber noch einmal erfinden möchte.
-
- User
- Beiträge: 29
- Registriert: Mittwoch 29. Oktober 2008, 21:29
- Wohnort: Chemnitz
- Kontaktdaten:
was ich machen will/wollte (wie auch immer) ist/war: in einer beliebig langen liste sollen alle möglichen kombinationen durchprobiert werden, bei denen ein element in der liste eine zahl von 1 bis 12 sein kann. weil das jetzt blöd ausgedrückt war, hier noch beispiele:
länge der liste = 1:
liste:
[1]
[2]
...
[11]
[12]
länge der liste = 2:
liste:
[1,1]
[1,2]
...
[1,11]
[1,12]
[2,1]
...
[12,12]
länge der liste = 3:
[1,1,1]
[1,1,2]
...
[12,12,12]
und so weiter
und bei jeder neuen kombination soll main() aufgerufen werden
länge der liste = 1:
liste:
[1]
[2]
...
[11]
[12]
länge der liste = 2:
liste:
[1,1]
[1,2]
...
[1,11]
[1,12]
[2,1]
...
[12,12]
länge der liste = 3:
[1,1,1]
[1,1,2]
...
[12,12,12]
und so weiter
und bei jeder neuen kombination soll main() aufgerufen werden
mind like a sieve
Aber die vorgestellte iterative Version macht ja mehr Funktionsaufrufe (xrange, reversed und vor allem divmod). Sie ist auch langsamer, sogar als die IMHO schlecht geschriebene Version des OP. Sie ruft sogar main() auf, was Blackjack "wegoptimiert" hat. abc2 ist dann Blackjacks Version. abc3 ist eine rekursive Version, leicht optimiert, ohne main():BlackVivi hat geschrieben:Rekursion ist nur ganz selten schneller. Du darfst nicht vergessen, dass jeder Funktionsaufruf auf einen Funktionsstack gepackt wird und sowas. Ein Funktionsaufruf ist keine triviale Angelegenheit - eine Schleife in Relation dazu schon.
Code: Alles auswählen
from timeit import Timer
setup = """liste = range(5)
def abc(zaehler):
if zaehler+1 < len(liste):
for liste[zaehler] in xrange(1,13):
zaehler += 1
abc(zaehler)
zaehler -= 1
else:
main()
def main(): pass
def abc2(liste, n=12):
length = len(liste) - 1
for i in xrange(n**length):
for j in reversed(xrange(length)):
i, k = divmod(i, n)
liste[j] = k + 1
def abc3(liste, zaehler, length):
if zaehler < length:
for liste[zaehler] in xrange(1, 13):
abc3(liste, zaehler + 1, length)
"""
print Timer("abc(0)", setup=setup).timeit(100)
print Timer("abc2(liste)", setup=setup).timeit(100)
print Timer("abc3(liste, 0, len(liste) - 1)", setup=setup).timeit(100)
Code: Alles auswählen
2.14144395447
8.58113614999
1.32978150854
Argh! Fakultät doch nicht! Überhaupt nicht schöner!BlackVivi hat geschrieben:Natürlich ist Rekursion insbesondere bei mathematischen Problemen viel schöner. Man stelle einfach mal das trivialbeispiel Fakultät vor. Das sieht rekursiv schon schöner als iterativ...
Natürlich sieht Fakultät rekursiv schöner aus als iterativ o_o Vielleicht liegt das ein bisschen im Auge des Betrachters, aber normalerweise....
@bords0: Tja, der letzte Beitrag vom OP ist der Grund, warum ich bei meiner ersten Implementierung nicht so ganz auf die Feinheiten eingegangen bin. Dachte mir schon dass das "Original" fehlerhaft war.
Bei Deiner letzten Lösung gefällt mir die API nicht. Das ist zu sehr auf "optimieren" getrimmt. Dem Aufrufer als drittes Argument "Länge der Liste - 1" aufzubürden finde ich nicht gut. Ich würde es dann eher so schreiben:
Wobei mich dieses "Mikrobenchmarking" nicht überzeugt, denn alleine der Aufruf einer Funktion, die nichts tut, aus diesem Konstrukt heraus schlägt bei mir bei `abc4` ca. 20% drauf. Wenn da auch noch irgend etwas sinnvolles passiert, bleibt die Frage wieviel, oder auch wie wenig, das zur Gesamtzeit beiträgt.
Bei Deiner letzten Lösung gefällt mir die API nicht. Das ist zu sehr auf "optimieren" getrimmt. Dem Aufrufer als drittes Argument "Länge der Liste - 1" aufzubürden finde ich nicht gut. Ich würde es dann eher so schreiben:
Code: Alles auswählen
def abc4(liste, zaehler=0):
if zaehler < (len(liste) - 1):
for liste[zaehler] in xrange(1, 13):
abc4(liste, zaehler + 1)
Zu was gibts itertools, damit braucht keiner Rekursion.
Hinweis: Für Python < 2.6 steht eine mögliche Implementierung in der Doku http://docs.python.org/library/itertool ... ls.product
Code: Alles auswählen
from timeit import Timer
setup = """liste = range(5)
from itertools import product
def main(): pass
def abc3(liste, zaehler, length):
if zaehler < length:
for liste[zaehler] in xrange(1, 13):
abc3(liste, zaehler + 1, length)
main()
def abc5(length):
for liste in product(range(1,13), repeat=length):
main()
"""
print Timer("abc3(liste, 0, len(liste) - 1)", setup=setup).timeit(100)
print Timer("abc5(4)", setup=setup).timeit(100)
Code: Alles auswählen
1.95250013462
0.961583572211