Данная статья является продолжением предыдущей статьи и также посвящена использованию битовой арифметики для решения задач с 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. Отметим также, что этот алгоритм использует константную дополнительную память.