昨日、リスト反転のためにいくつかの異なる方法を示すために、リストの2つの逆関数を書きました。しかし、分岐関数がより多くの呼び出しを終わらせ、同じ数の呼び出し(マイナス1)が重要でないにもかかわらず、分岐再帰(rev2
)を使用する関数が実際には線形再帰を使用する関数線形回帰関数の自明でない呼び出しよりも(実際にはより多くの計算を要する)呼び出しである。明示的に並列処理を起動しているところはありません。そのため、パフォーマンスの差はどこから来ていますか。分岐再帰が線形再帰よりも高速である理由(例:リスト反転)
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))
例コール:
$ 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
各呼び出しはリストをコピーするので、コピーは* O(n)*で行われます。 –
しかし、私は両方の機能でそれを行います。分岐バージョンでは、いくつかの '並列'呼び出しで負荷を分割するだけです。 –
2つのルーチンの関数呼び出しの数を数えるだけでなく、各ルーチンのすべての呼び出しでコピーされたリスト項目の合計数をカウントアップします。 –