Подтвердить что ты не робот

Экспоненты в python x. ** y vs math.pow(x, y)

Какой из них более эффективен с использованием math.pow или оператора **? Когда я должен использовать один над другим?

До сих пор я знаю, что x**y может возвращать int или float, если вы используете десятичную функция pow вернет float

import math

print math.pow(10, 2)

print 10. ** 2
4b9b3361

Ответ 1

Использование силового оператора ** будет быстрее, поскольку у него не будет накладных расходов на вызов функции. Вы можете увидеть это, если разобрать код Python:

>>> dis.dis('7. ** i')
  1           0 LOAD_CONST               0 (7.0) 
              3 LOAD_NAME                0 (i) 
              6 BINARY_POWER         
              7 RETURN_VALUE         
>>> dis.dis('pow(7., i)')
  1           0 LOAD_NAME                0 (pow) 
              3 LOAD_CONST               0 (7.0) 
              6 LOAD_NAME                1 (i) 
              9 CALL_FUNCTION            2 (2 positional, 0 keyword pair) 
             12 RETURN_VALUE         
>>> dis.dis('math.pow(7, i)')
  1           0 LOAD_NAME                0 (math) 
              3 LOAD_ATTR                1 (pow) 
              6 LOAD_CONST               0 (7) 
              9 LOAD_NAME                2 (i) 
             12 CALL_FUNCTION            2 (2 positional, 0 keyword pair) 
             15 RETURN_VALUE         

Обратите внимание, что Im использует переменную i в качестве экспоненты здесь, потому что константные выражения, такие как 7. ** 5, фактически вычисляются во время компиляции.

Теперь, на практике, это различие не так важно, как вы можете видеть, когда это время:

>>> from timeit import timeit
>>> timeit('7. ** i', setup='i = 5')
0.2894785532627111
>>> timeit('pow(7., i)', setup='i = 5')
0.41218495570683444
>>> timeit('math.pow(7, i)', setup='import math; i = 5')
0.5655053168791255

Итак, в то время как pow и math.pow примерно в два раза медленнее, они все еще достаточно быстры, чтобы не беспокоиться. Если вы не сможете на самом деле идентифицировать возведение в степень как узкое место, то не будет причины выбирать один метод над другим, если ясность уменьшается. Это особенно справедливо, поскольку pow предлагает интегрированную модульную операцию, например.


Альф спросил хороший вопрос в комментариях выше:

timeit показывает, что math.pow во всех случаях медленнее, чем **. Что такое math.pow() хорошо в любом случае? Кто-нибудь знает, где это может быть какое-то преимущество?

Большая разница math.pow как встроенного pow, так и силового оператора ** заключается в том, что он всегда использует семантику float. Поэтому, если вы по какой-то причине хотите убедиться, что вы вернете поплавок в результате, тогда math.pow обеспечит это свойство.

Давайте рассмотрим пример: у нас есть два числа, i и j, и понятия не имею, являются ли они поплавками или целыми числами. Но мы хотим получить результат с плавающей точкой i^j. Итак, какие у нас есть варианты?

  • Мы можем преобразовать хотя бы один из аргументов в float, а затем сделать i ** j.
  • Мы можем сделать i ** j и преобразовать результат в float (float exponentation автоматически используется, когда i или j являются float, поэтому результат тот же).
  • Мы можем использовать math.pow.

Итак, давайте протестируем это:

>>> timeit('float(i) ** j', setup='i, j = 7, 5')
0.7610865891750791
>>> timeit('i ** float(j)', setup='i, j = 7, 5')
0.7930400942188385
>>> timeit('float(i ** j)', setup='i, j = 7, 5')
0.8946636625872202
>>> timeit('math.pow(i, j)', setup='import math; i, j = 7, 5')
0.5699394063529439

Как вы можете видеть, math.pow на самом деле быстрее! И если вы подумаете об этом, накладные расходы от вызова функции также исчезли, потому что во всех остальных альтернативах мы должны называть float().


Кроме того, можно отметить, что поведение ** и pow может быть переопределено путем реализации специального метода __pow____rpow__) для настраиваемых типов. Поэтому, если вы этого не хотите (по какой-либо причине), использование math.pow не сделает этого.

Ответ 2

Только для протокола: оператор ** вызывает встроенную функцию pow, которая принимает необязательный третий аргумент (модуль) если первые два аргумента являются целыми типами.

Итак, если вы собираетесь вычислять остатки от полномочий, используйте встроенную функцию. math.pow может дать вам ложные результаты:

import math

base = 13
exp = 100
mod = 2
print math.pow(base, exp) % mod
print pow(base, exp, mod)

Когда я запустил это, я получил 0.0 в первом случае, который, очевидно, не может быть правдой, потому что 13 нечетно (и, следовательно, все его интегральные мощности). Версия math.pow использует ограниченную точность, которая вызывает ошибку.

Для справедливости, мы должны сказать, math.pow может быть намного быстрее:

import timeit
print timeit.timeit("math.pow(2, 100)",setup='import math')
print timeit.timeit("pow(2, 100)")

Вот что я получаю как вывод:

0.240936803195
1.4775809183

Некоторые онлайн-примеры

Ответ 3

Ну, они для разных задач, действительно.

Используйте pow (эквивалентно x ** y с двумя аргументами), когда вам нужна целочисленная арифметика.

И используйте math.pow, если любой аргумент float, и вы хотите вывести float.

Для обсуждения различий между pow и math.pow см. этот вопрос.

Ответ 4

** действительно быстрее, чем math.pow(), но если вам нужна простая квадратичная функция, например, в вашем примере, еще быстрее использовать продукт.

10.*10.

будет быстрее, чем

10.**2

Разница невелика и не примечательна одной операцией (с использованием timeit), но с большим количеством операций она может быть значительной.