2014-01-07 8 views
30

Какой из них более эффективен с использованием math.pow или оператора **? Когда я должен использовать один над другим?Экспоненты в python x. ** y vs math.pow (x, y)

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

import math 

print math.pow(10, 2) 

print 10. ** 2 
+7

Почему бы не 'timeit' узнать? – mgilson

+0

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

+2

Это вряд ли имеет значение. Ваш код будет узким местом, а не этими функциями. – duffymo

ответ

52

Использование оператора электропитания ** будет быстрее, так как оно не будет иметь накладных расходов на вызов функции. Вы можете увидеть это, если вы разобрать код 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   

Обратите внимание, что я использую переменную 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 и оператор питания ** является то, что он всегда использует всплывают семантику. Поэтому, если вы по какой-то причине хотите убедиться, что вы вернете поплавок, то math.pow обеспечит это свойство.

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

  • Мы можем преобразовать хотя бы один из аргументов в поплавок, а затем сделать i ** j.
  • Мы можем сделать и преобразовать результат в поплавок (показатель экспоненциальности float автоматически используется, когда i или j являются поплавками, поэтому результат тот же).
  • Мы можем использовать 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 этого не сделает.

3

Ну, они для разных задач, на самом деле.

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

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

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

+0

'__builtin __. Pow' может более четко указать, что это встроенный -in, но если OP уже не знает о модуле '__builtin__', это, вероятно, будет путать. – user2357112

4

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

Итак, если вы намерены вычислять остатки от полномочий, используйте встроенную функцию. 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 

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

1

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

10.*10. 

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

10.**2 

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

Смежные вопросы