Данная статья является продолжением предыдущей статьи и также посвящена использованию битовой арифметики для решения задач с LeetCode.
На этот раз рассмотрим задачу 50. Pow(x, n) в которой нас просят реализовать алгоритм возведения произвольного числа x в целую степень n. Ограничения заданные в условиях:
Очевидно, что первым на ум приходит наивный алгоритм, где нам надо сделать n-1 умножение и вернуть результат. Этот алгоритм работает за O(n) и прост в понимании.
Но, если нам предстоит много операций возведения в степень, то хочется его как-то улучшить. Для этого можно воспользоваться тем фактом, что любое положительное n мы можем представить в бинарном виде, для того, чтобы урезать вычислительную сложность до O(log n)
Осталось реализовать это решение:
def my_pow(x: float, n: int) -> float: if n == 0: return 1.0 # Обработка отрицательной степени if n < 0: x = 1 / x n = -n result = 1.0 current_product = x while n > 0: if n & 1: # нашли отличный от 1 множитель в нашем разложении result *= current_product current_product *= current_product n = n >> 1 return result
В результате, вместо n-1 умножений наивного алгоритма, нам надо сделать всего лишь количество итераций, равное бинарному представлению числа n, что дает вычислительную сложность O(log n) и константную сложность по памяти.
Теперь, когда мы умеем быстро возводить в произвольную степень, давайте решим похожую задачу: 372. Super Pow в которой нас просят посчитать
при условии, что b - экстремально большое положительное целое число, заданное в виде массива, а также:
Основная сложность заключается в том, что b - очень большое, но нам может помочь тот факт, что в задаче от нас требуется предоставить результат деления по модулю в качестве ответа, что означает, что ответ будет в диапазоне от 0 до 1337
Опять воспользуемся тем фактом, что мы способны представить b в виде произведения степеней 2.
class Solution: def super_pow(self, a: int, b: list[int]) -> int: MOD = 1337 def pow_mod(base: int, exponent: int) -> int: result = 1 base = base % MOD while exponent > 0: if exponent & 1: result = (result * base) % MOD base = (base * base) % MOD exponent = exponent >> 1 return result result = 1 a = a % MOD for digit in b: result = (pow_mod(result, 10) * pow_mod(a, digit)) % MOD return result
В результате получаем алгоритм, который за O(n log k), где n - число десятичных разрядов числа b (длина массива), а k - количество разрядов в бинарном представлении цифры, то есть от 0 до 4. Отметим также, что этот алгоритм использует константную дополнительную память.
