Gestern schrieb ich zwei mögliche umgekehrte Funktionen für Listen, um einige verschiedene Möglichkeiten zur Listeninversion zu demonstrieren. Aber dann habe ich bemerkt, dass die Funktion mit Verzweigung Rekursion (rev2
) ist tatsächlich schneller als die Funktion mit linearen Rekursion(), obwohl die Verzweigung Funktion mehr Anrufe zu beenden und die gleiche Anzahl von Anrufen (minus eins) von nicht-trivialen dauert Aufrufe (die eigentlich rechenintensiver sind) als die nicht-trivialen Aufrufe der linear rekursiven Funktion. Nirgends löse ich explizit Parallelität aus, woher kommt dann der Leistungsunterschied, der eine Funktion mit mehr Anrufen macht, die mehr Zeit benötigen?Warum Verzweigung Rekursion schneller als lineare Rekursion (Beispiel: Listeninversion)
from sys import argv
from time import time
nontrivial_rev1_call = 0 # counts number of calls involving concatentation, indexing and slicing
nontrivial_rev2_call = 0 # counts number of calls involving concatentation, len-call, division and sclicing
length = int(argv[1])
def rev1(l):
global nontrivial_rev1_call
if l == []:
return []
nontrivial_rev1_call += 1
return rev1(l[1:])+[l[0]]
def rev2(l):
global nontrivial_rev2_call
if l == []:
return []
elif len(l) == 1:
return l
nontrivial_rev2_call += 1
return rev2(l[len(l)//2:]) + rev2(l[:len(l)//2])
lrev1 = rev1(list(range(length)))
print ('Calls to rev1 for a list of length {}: {}'.format(length, nontrivial_rev1_call))
lrev2 = rev2(list(range(length)))
print ('Calls to rev2 for a list of length {}: {}'.format(length, nontrivial_rev2_call))
print()
l = list(range(length))
start = time()
for i in range(1000):
lrev1 = rev1(l)
end = time()
print ("Average time taken for 1000 passes on a list of length {} with rev1: {} ms".format(length, (end-start)/1000*1000))
start = time()
for i in range(1000):
lrev2 = rev2(l)
end = time()
print ("Average time taken for 1000 passes on a list of length {} with rev2: {} ms".format(length, (end-start)/1000*1000))
Beispielaufruf:
$ python reverse.py 996 calls to rev1 for a list of length 996: 996 calls to rev2 for a list of length 996: 995 Average time taken for 1000 passes on a list of length 996 with rev1: 7.90629506111145 ms Average time taken for 1000 passes on a list of length 996 with rev2: 1.3290061950683594 ms
Bei jedem Aufruf kopieren Sie die Liste und das Kopieren erfolgt in * O (n) *. –
Das mache ich aber in beiden Funktionen. Die Verzweigungsversion teilt die Last lediglich auf mehrere parallele Aufrufe auf. –
Anstatt nur die Anzahl der Funktionsaufrufe in Ihren beiden Routinen zu zählen, zählen Sie die Gesamtzahl der Listenelemente, die für alle Aufrufe in jeder Routine kopiert wurden. –