Использование экспоненциальности ** 0,5 менее эффективно, чем математика.

Цитата из Программирование на Python: введение в компьютерную науку

Мы могли бы взять квадратный корень используя возведение в степень **. С помощью math.sqrt несколько эффективнее.

"Несколько", но в какой степени и как?

Ответ 1

Теоретически hammar answer и duffymo answer - хорошие догадки. Но на практике, на моей машине, это не более эффективно:

>>> 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

Частью проблемы является операция .. Если вы импортируете sqrt непосредственно в пространство имен, вы получите небольшое улучшение.

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

Ключевые слова: незначительные.

Дальнейшее тестирование показывает, что по мере увеличения числа выигрыш от использования sqrt увеличивается. Но все же не так много!

>>> 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

Ответ 2

Не нужно угадывать реализацию, мы можем прочитать код!

math.sqrt представляет собой тонкую оболочку около sqrt из стандартной библиотеки C: см. mathmodule.c, строка 956

Оператор ** имеет несколько реализаций в зависимости от типов аргументов, но в случае показателя с плавающей запятой он в конечном итоге отправляет в pow из стандартной библиотеки C (см. floatobject.c строка 783).

Современные процессоры часто имеют специальные квадратные корневые команды, общие подпрограммы которых не используются (сравнить и сопоставить реализации pow и sqrt в glibc для x86-64, например). Но после того, как добавлены дополнительные служебные данные интерпретатора (байт-коды, проверка типов, отправка метода и т.д.), Разница в скорости загрузки не имеет большого значения, и в них могут доминировать такие проблемы, как прямое обращение к sqrt или просмотр через модуль math (как показывают тайминги в других ответах).

Ответ 3

** должен поддерживать любую экспоненту, а math.sqrt всегда знает 0.5. Таким образом, math.sqrt может использовать более специализированный (и, следовательно, более эффективный) алгоритм.

Ответ 4

Я предполагаю, что math.sqrt использует метод Ньютона, который сходится квадратично, а экспоненциация использует что-то еще медленнее.

Ответ 5

Вот несколько иной подход. Мы хотим, чтобы int больше квадратного корня. Два способа (которые не согласны с квадратными числами, но это ОК):

>>>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

Итак, математические функции быстрее... пока вы не конвертируете float в int. (Мне нужно сделать много сравнений со значением, и пока я его не тестировал, сравнение целых чисел должно быть дешевле, чем сравнение поплавков.)

Но эй, это Питон. Вы используете слишком много абстракций, чтобы попытаться оптимизировать производительность с этим уровнем детализации.