Экспоненты в питоне: x**y против math.pow(x, y)

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

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

import math

print( math.pow(10, 2) )

print( 10. ** 2 )

person user3084006    schedule 07.01.2014    source источник
comment
Почему бы не timeit узнать?   -  person mgilson    schedule 07.01.2014
comment
timeit показывает, что math.pow медленнее, чем ** во всех случаях. На что вообще годится math.pow()? Кто-нибудь знает, где это может быть выгодно?   -  person Alfe    schedule 07.01.2014
comment
@Alfe, как ты измерил, или я неправильно понял твой in all cases? Я вижу случаи, когда math.pow намного быстрее.   -  person Wolf    schedule 16.02.2015
comment
@Волк, а ты? Ваша ссылка указывает на сравнение pow с math.pow, но я говорю о сравнении math.pow с оператором **. Ваше сравнение может быть завершено добавлением этой третьей версии, тогда ** снова превосходит любой другой вариант: import timeit; print timeit.timeit("math.pow(2, 100)",setup='import math'), timeit.timeit("pow(2, 100)"), timeit.timeit("2 ** 100")0.170357942581 1.00546097755 0.013473033905.   -  person Alfe    schedule 17.02.2015
comment
Но я должен признать, что результаты не совсем одинаковы. math.pow всегда возвращает float (также для ввода int), а ** возвращает тип своего ввода (ну, смешанный ввод приводит к float). Но это все еще не повод использовать math.pow, ИМХО.   -  person Alfe    schedule 17.02.2015
comment
@Alfe ВАУ спасибо, что указали на эту деталь. Есть ли простое объяснение (кроме ссылки на исходный код)? ... может быть, хороший вопрос для ТАК ;-)?   -  person Wolf    schedule 17.02.2015
comment
@Alfe, даже если найден ответ на вопрос Почему a*b намного быстрее, чем math.pow(a,b)? - и мне очень не нравится импортировать математику для pow - я не уверен, что там что-то осталось. И *ваш вопрос (What is math.pow() good for anyway?), похоже, еще не получил ответа. В любом случае, большое спасибо за ваше время :-)   -  person Wolf    schedule 17.02.2015
comment
Все еще изучаю это, но ответы здесь кажутся неполными. Я выполнял довольно большие криптографические вычисления, и хотя 4926601444670 ** 4235309647073 % 6000029000033 вызывает сбой интерпретатора, pow(4926601444670 , 4235309647073, 6000029000033) происходит мгновенно.   -  person temporary_user_name    schedule 01.05.2016
comment
Да, посмотрите это stackoverflow.com/questions/14133806/   -  person temporary_user_name    schedule 01.05.2016


Ответы (5)


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


Alfe задал хороший вопрос в комментариях выше:

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

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

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

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

person poke    schedule 07.01.2014
comment
Вы получили +1 за поведение **, и pow можно переопределить: если вы пишете модуль, я уверен, что очень важно предотвратить неожиданности, исходящие от пользовательской среды.... - person Xerix; 25.02.2021

Функция pow() позволит вам добавить третий аргумент в качестве модуля.

Например: недавно я столкнулся с ошибкой памяти при выполнении

2**23375247598357347582 % 23375247598357347583

Вместо этого я сделал:

pow(2, 23375247598357347582, 23375247598357347583)

Это возвращает всего за миллисекунды вместо огромного количества времени и памяти, которые занимает простая экспонента. Таким образом, при работе с большими числами и параллельным модулем pow() более эффективен, однако при работе с меньшими числами без модуля ** более эффективен.

person bravoalpha90    schedule 02.12.2019

Ну, они для разных задач, правда.

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

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

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

person wim    schedule 07.01.2014

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

10.*10.

тогда будет быстрее

10.**2

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

person KDhoore    schedule 11.05.2015

Просто для протокола: оператор ** эквивалентен двухаргументной версии встроенной функции pow, 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 использует ограниченную точность IEEE-754 двойной точности (52 бита мантисса, чуть меньше 16 знаков после запятой), что вызывает здесь ошибку.

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

>>> import timeit
>>> min(timeit.repeat("pow(1.1, 9.9)", number=2000000, repeat=5))
0.3063715160001266
>>> min(timeit.repeat("math.pow(1.1, 9.9)", setup="import math", number=2000000, repeat=5))
0.2647279420000359

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


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


Обновление (неизбежное исправление):
Я удалил временное сравнение math.pow(2,100) и pow(2,100), так как math.pow дает неправильный результат, тогда как, например, сравнение между pow(2,50) и math.pow(2,50) было бы справедливым (хотя и не реалистичное использование функции math-module). Я добавил лучший, а также детали, которые вызывают ограничение math.pow.

person Wolf    schedule 16.02.2015