В Дзене Python есть принцип, согласно которому "должен существовать один и желательно только один очевидный способ сделать это". Однако в Python есть как минимум три способа возведения числа в степень:
оператор
**
встроенная функция
pow()
функция
math.pow()
В этой статье мы рассмотрим каждый из способов и разберемся, чем они отличаются.
Оператор **
Оператор **
— это классический оператор возведения в степень. Это в принципе первое, что обычно приходит в голову любому программисту на Python, когда требуется возвести число в степень.
Приведенный ниже код:
print(2 ** 10) # 2 в 10-й степени
print(10 ** 2) # 10 во 2-й степени
выводит:
1024
100
Важно отметить, что оператор **
работает со всеми числовыми типами в Python: int, float, Fraction, Decimal, complex
.
Приведенный ниже код:
from fractions import Fraction
from decimal import Decimal
nums = [100, 0.3, Fraction(2, 5), Decimal('0.3'), complex(3, 7)]
for num in nums:
print(num ** 2)
выводит:
10000
0.09
4/25
0.09
(-40+42j)
Следует сказать, что оператор возведения в степень **
является правоассоциативным, то есть выражение x ** y ** z
вычисляется справа налево и трактуется как x ** (y ** z)
, а не как (x ** y) ** z
.
Приведенный ниже код:
print(2 ** 3 ** 4) # 2 в 81-й степени
print((2 ** 3) ** 4) # 8 в 4-й степени
выводит:
2417851639229258349412352
4096
Встроенная функция pow()
Встроенная функция pow()
принимает два обязательных аргумента, первый из которых base
— это число, которое необходимо возвести в определенную степень, а второй exp
— собственно, сама степень.
Приведенный ниже код:
print(pow(2, 10)) # 2 в 10-й степени
print(pow(10, 2)) # 10 во 2-й степени
выводит:
1024
100
Следует отметить, что функция pow()
, как и оператор **
, работает со всеми числовыми типами в Python, а также имеет аналогичную производительность, то есть выполняется примерно за такое же время.
Возникает резонный вопрос: зачем использовать данную функцию, когда есть простой и понятный оператор **
? Дело в том, что функция pow()
также может принимать необязательный аргумент mod
, с помощью которого она возводит число в степень по модулю. Другими словами, она возвращает число, которое является остатком от деления результата возведения в степень на число mod
.
Приведенный ниже код:
print(pow(2, 10, 15)) # 2 в 10-й степени по модулю 15
print(pow(10, 2, 15)) # 10 во 2-й степени по модулю 15
выводит:
4
10
Несложно заметить, что аналогичного результата можно добиться с использованием связки операторов **
и %
.
Приведенный ниже код:
print(2 ** 10 % 15) # 2 в 10-й степени по модулю 15
print(10 ** 2 % 15) # 10 в 2-й степени по модулю 15
выводит:
4
10
И снова возникает вопрос: зачем использовать функцию pow()
, если можно легко обойтись операторами **
и %
? Оказывается, в отличие от оператора **
, функция pow()
при возведении числа в степень по модулю за кулисами использует оптимизированный алгоритм, который не вычисляет результат возведения в степень явно, как это делает оператор **
.
Приведенный ниже код:
from timeit import timeit
power_by_stars = '''
number = 2
power = 10000
number ** power % 100
'''
power_by_pow = '''
number = 2
power = 10000
pow(number, power, 100)
'''
print('Возведение в степень по модулю с помощью оператора **:', timeit(power_by_stars, number=1_000_000), 'с.')
print('Возведение в степень по модулю с помощью функции pow():', timeit(power_by_pow, number=1_000_000), 'с.')
выводит (результат может незначительно отличаться):
Возведение в степень по модулю с помощью оператора **: 10.867430499754846 с.
Возведение в степень по модулю с помощью функции pow(): 0.17797810025513172 с.
В результате приведенного эксперимента получили, что функция pow()
выполняет операцию возведения в степень по модулю намного быстрее операторов **
и %
. Ее время выполнения примерно в 60
раз меньше времени выполнения связки операторов **
и %
.
Возведение в степень по модулю — это операция, которая играет ключевую роль в областях, связанных с криптографией, теорией чисел, алгоритмами шифрования и безопасной передачей данных. Она лежит в основе алгоритма RSA, который используется для шифрования и дешифрования сообщений.
Исходный код функции pow()
доступен по ссылке. Python использует алгоритм быстрого возведения в степень, схему "справа налево".
Функция math.pow()
Третий способ возведения в степень подразумевает использование функции pow()
встроенного модуля math
. Эта функция также принимает два аргумента: число и степень — и возвращает результат возведения числа в степень.
Приведенный ниже код:
import math
print(math.pow(2, 10)) # 2 в 10-й степени
print(math.pow(10, 2)) # 10 во 2-й степени
выводит:
1024.0
100.0
В отличие от оператора **
и встроенной функции pow()
, функция math.pow()
всегда неявно преобразует входные аргументы в вещественные числа (тип float
) и соответственно возвращает результат в виде такого же вещественного числа.
Учитывая эту особенность, функцию math.pow()
необходимо применять с осторожностью. Как известно, вещественные числа (тип float
) имеют ограниченный диапазон значений в отличие от тех же целых чисел (тип int
). Поэтому если результат возведения в степень окажется достаточно большим, то это приведет к возникновению ошибки переполнения.
Приведенный ниже код:
import math
print(math.pow(2, 10000))
приводит к возникновению ошибки переполнения:
OverflowError: math range error
Следует также отметить, что функция pow()
встроенного модуля math
не поддерживает возведение в степень комплексных чисел, поскольку не существует неявного преобразования из комплексного числа (тип complex
) в вещественное (тип float
).
Заключение
Выбирая между встроенными способами возведения в степень, следует отдать предпочтение тем, которые окажутся наиболее полезны и эффективны в той или иной ситуации.
Например, оператор **
позволяет быстро выполнить возведение в степень для получения результата "на лету", а встроенная функция pow()
позволяет выполнить возведение в степень по модулю. Функция math.pow()
не является достаточно точной, однако она все равно может оказаться полезной при вычислении небольших степеней, особенно если результат необходим в виде вещественного числа.
А вы знали, что оператор **
как оператор возведения в степень впервые появился в языке программирования Fortran (Formula Translation)? Возможно, именно поэтому создатели Python решили использовать именно его вместо общепринятого символа каретки ^
, который в Python отвечает за оператор побитового исключающего ИЛИ (XOR).
Присоединяйтесь к нашему телеграм-каналу, будет интересно и познавательно!
❤️Happy Pythoning!?
Комментарии (54)
CrazyElf
25.06.2024 06:41+4Если модуль не планируется брать, то следует выбирать оператор возведения в степень
**
. Потому что использование встроенной функцииpow
требует сначала найти её по имени, а потом уже вызвать. Вы можете вообще присвоить в имяpow
что-то своё, так работает Питон. Поэтому сначала из переменной берётся ссылка на функцию, и потом эта функция вызывается. А оператор таких телодвижений не требует, он вызывается сразу. Хотя у разных интерпретаторов Питона могут быть разные оптимизации, где-то может и не будет разницы и код скомпилируется в одно и то же. Но по классике разница есть и какие-то доли процента производительности на этом можно выиграть. Очень рекомендую модульdis
, много интересного можно узнать, сравнивая похожий с виду код, во что он реально компилируется.
printf
25.06.2024 06:41общепринятого символа каретки
^
Теперь общепринятый уже, видимо,
**
, т.к. Питон, Жаваскрипт и Руби. :)adeshere
25.06.2024 06:41+11Теперь общепринятый уже, видимо,
**
, т.к. Питон, Жаваскрипт и Руби. :)Эх, молодежь ;-) Вообще-то в самых первых языках программирования, начиная с фортрана, было именно "**". Символ "^" стап использоваться заметно позже, и до сих пор уступает варианту "**" по количеству выбравших его "языков" (статья там длинная, см. подраздел "В языках программирования"):
-
x ** y
:Fortran, Ada, Z shell, KornShell, Bash, COBOL, CoffeeScript, FoxPro, Gnuplot, Groovy, JavaScript, OCaml, F #, Perl, PHP, PL / I, Python, Rexx, Ruby, SAS, Seed7, Tcl, ABAP, Mercury, Haskell (для показателей с плавающей запятой), Turing и VHDL.
x ^ y
:
AWK, BASIC, J, MATLAB, Wolfram Language (Mathematica), R, Microsoft Excel, Analytica, TeX (и его производные), TI-BASIC, bc (для целых показателей), Haskell (для неотрицательных целых показателей), Lua и большинство систем компьютерной алгебры.
А "второй древнейший" так и вовсе только через библиотечную функцию... Ну и особенно весело с бейсиком: там разные диалекты использовали разные символы ;-)
printf
25.06.2024 06:41Думаю, оно из литературы "общепринятое", да.
В лагере
x ^ y
еще Julia вроде.Вот молодежью меня давненько не величали, спасибо :з
-
developerre
Не знал про правоассоциативность оператора **. А почему решили сделать его таким?
artptr86
Потому что в математике возведение в степень правоассоциативно
wyfinger
Matlab, например, считает по другому: https://github.com/wyfinger/WEL-Script/wiki/Power-function-priority
ToyJoy
Возможно из за того что: (a^b)^c = a^(b*c)