Цитата из Программирование на Python: введение в компьютерную науку
Мы могли бы взять квадратный корень используя возведение в степень **. С помощью math.sqrt несколько эффективнее.
"Несколько", но в какой степени и как?
Цитата из Программирование на Python: введение в компьютерную науку
Мы могли бы взять квадратный корень используя возведение в степень **. С помощью math.sqrt несколько эффективнее.
"Несколько", но в какой степени и как?
Теоретически 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
Не нужно угадывать реализацию, мы можем прочитать код!
math.sqrt
представляет собой тонкую оболочку около sqrt
из стандартной библиотеки C: см. mathmodule.c
, строка 956
Оператор **
имеет несколько реализаций в зависимости от типов аргументов, но в случае показателя с плавающей запятой он в конечном итоге отправляет в pow
из стандартной библиотеки C (см. floatobject.c
строка 783).
Современные процессоры часто имеют специальные квадратные корневые команды, общие подпрограммы которых не используются (сравнить и сопоставить реализации pow
и sqrt
в glibc для x86-64, например). Но после того, как добавлены дополнительные служебные данные интерпретатора (байт-коды, проверка типов, отправка метода и т.д.), Разница в скорости загрузки не имеет большого значения, и в них могут доминировать такие проблемы, как прямое обращение к sqrt
или просмотр через модуль math
(как показывают тайминги в других ответах).
**
должен поддерживать любую экспоненту, а math.sqrt
всегда знает 0.5
. Таким образом, math.sqrt
может использовать более специализированный (и, следовательно, более эффективный) алгоритм.
Я предполагаю, что math.sqrt использует метод Ньютона, который сходится квадратично, а экспоненциация использует что-то еще медленнее.
Вот несколько иной подход. Мы хотим, чтобы 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. (Мне нужно сделать много сравнений со значением, и пока я его не тестировал, сравнение целых чисел должно быть дешевле, чем сравнение поплавков.)
Но эй, это Питон. Вы используете слишком много абстракций, чтобы попытаться оптимизировать производительность с этим уровнем детализации.