Способы возведения в степень в Python

в 6:29, , рубрики: python, Алгоритмы, возведение в степень, дзен, математика, Программирование, производительность, функция, числа

В Дзене 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!🐍

Автор: Тимур Гуев

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js