2011-07-09 25 views

Odpowiedz

15

Teoretycznie, hammar's answer i duffymo's answer są dobrymi przypuszczeniami. Jednak w praktyce, na moim komputerze, to nie bardziej efektywne:

>>> import timeit 
>>> timeit.timeit(stmt='[n ** 0.5 for n in range(100)]', setup='import math', number=10000) 
0.15518403053283691 
>>> timeit.timeit(stmt='[math.sqrt(n) for n in range(100)]', setup='import math', number=10000) 
0.17707490921020508 

Częścią problemu jest operacja .. Jeśli importujesz plik sqrt bezpośrednio do przestrzeni nazw, uzyskujesz niewielką poprawę.

>>> timeit.timeit(stmt='[sqrt(n) for n in range(100)]', setup='from math import sqrt', number=10000) 
0.15312695503234863 

Słowo kluczowe istnieje: niewielkie.

Dalsze testowanie wskazuje, że wraz ze wzrostem liczby zwiększa się korzyść, którą można uzyskać dzięki użyciu sqrt. Ale wciąż nie przez wiele!

>>> timeit.timeit(stmt='[n ** 0.5 for n in range(1000000)]', setup='import math', number=1) 
0.18888211250305176 
>>> timeit.timeit(stmt='[math.sqrt(n) for n in range(1000000)]', setup='import math', number=1) 
0.18425297737121582 
>>> timeit.timeit(stmt='[sqrt(n) for n in range(1000000)]', setup='from math import sqrt', number=1) 
0.1571958065032959 
+0

Doszedłem do tych samych wniosków. – zneak

1

** musi obsługiwać każdy wykładnik, podczas gdy math.sqrt wie, że zawsze jest to 0.5. math.sqrt może zatem używać bardziej wyspecjalizowanego (a zatem prawdopodobnie bardziej wydajnego) algorytmu.

+0

Optymalna implementacja '**' może po prostu rozgałęzić do 'math.sqrt', jeśli wykładnik jest mniejszy niż 1. To prawdopodobnie miałoby ledwie mierzalny wpływ. – zneak

+0

@zneak: Większość implementacji * do *. –

+0

@zneak: Mimo to musi wykonać ten test, więc zawsze będzie (choć nieznacznie) wolniejszy. – hammar

1

Domyślam się, że Math.sqrt wykorzystuje Newton's method, który zbiega kwadratowo i potęgowanie używa czegoś innego, który jest wolniejszy.

+0

Jak zauważono również w komentarzach: nie ma powodu, aby expotentacja nie korzystała z tego samego algorytmu lub po prostu ponownie wykorzystała istniejącą implementację, dla expotentation o 0,5. – delnan

+1

'math.sqrt' jest prawdopodobnie aliasem dla funkcji matematycznej C' sqrt', która jest zaimplementowana przy użyciu najlepszego algorytmu dla twojej platformy. Jeśli twój procesor obsługuje instrukcje SSE, otrzymujesz rodzinę instrukcji 'sqrt *', której wszyscy członkowie są tak szybcy, jak tylko się da. – zneak

11

Nie trzeba zgadywać realizacji, możemy odczytać kod!

math.sqrt jest cienką owijki wokół sqrt ze standardowej biblioteki C: patrz mathmodule.c, line 956

Operator ** ma wiele implementacje, w zależności od typów argumentów, ale w przypadku zmiennoprzecinkowej wykładnik, ostatecznie wywołania do pow ze standardowej biblioteki C (patrz floatobject.c line 783).

Nowoczesne procesory często mają specjalne instrukcje dotyczące pierwiastków kwadratowych, których nie używają ogólne procedury potęgowania (porównaj i porównuj implementacje pow i sqrt w glibc dla x86-64, na przykład). Ale po dodaniu wszystkich kosztów obsługi interpretatora (kody bajtów, sprawdzanie typu, wysyłanie metod itp.) Różnica w prędkości początkowej nie ma większego znaczenia i może być zdominowana przez takie problemy, jak zadzwonienie pod numer sqrt bezpośrednio lub wyszukiwanie za pośrednictwem moduł math (co pokazują czasy w innych odpowiedziach).

1

Oto nieco inne podejście. Chcemy, aby int był większy niż pierwiastek kwadratowy. Dwa sposoby (które nie zgadzają się z numerami kwadratowych ale to jest OK):

>>>timeit.timeit(stmt='[int(n**0.5)+1 for n in range(1000000)]', setup='', number=1) 
0.481772899628 
>>>timeit.timeit(stmt='[ceil(sqrt(n)) for n in range(1000000)]', setup='from math import sqrt, ceil', number=1) 
0.293844938278 
>>>timeit.timeit(stmt='[int(ceil(sqrt(n))) for n in range(1000000)]', setup='from math import sqrt, ceil', number=1) 
0.511347055435 

Więc funkcje matematyczne są szybciej ... aż przekonwertować pływaka do int.(Muszę wykonać wiele porównań z wartością, a gdy tego nie testowałem, porównywanie liczb całkowitych powinno być tańsze niż porównywanie pływaków.)

Ale hej, to Python. Masz zbyt wiele abstrakcji, by spróbować zoptymalizować wydajność przy takim poziomie szczegółowości.

Powiązane problemy