На этом шаге мы рассмотрим особенности такой реализации рекурсивной функции.
Существует возможность реализовать более эффективные алгоритмы вычисления степени за логарифмическое время, если на этапе декомпозиции разбить задачу пополам - на две подзадачи в половину размера исходной. Поскольку n - неотрицательное целое число, нам понадобятся две схемы в зависимости от четности n. Если n - чётное, то согласно рекурсивному подходу процесс можно представить следующим образом:
Таким образом, чтобы получить bn, результат подзадачи нужно возвести в квадрат. И наоборот, если n - нечётное, следует рассмотреть подзадачу с целочисленным размером (n - 1)/2. Её схема:
В этом случае результат подзадачи тоже возводится в квадрат, после чего нужно умножить его на основание b. С этими рекурсивными условиями и тривиальным начальным условием рекурсивная функция выглядит следующим образом:
1, если n = 0, f (b,n) = f(b, n/2)2, если n > 0 и n - чётное, f(b, (n - 1)/2)2 * b, если n > 0 и n - нечётное.
Правильная реализация линейно-рекурсивной функции показана в примере 4.3. Целочисленное деление (//) вместо вещественного деления (/) не обязательно и применено, чтобы подчеркнуть, что второй входной параметр - целое число. Кроме того, если n - нечётное, то (n - 1)//2 = n//2. Однако в коде используется первое выражение, поскольку оно математически точнее.
1 2 3 4 5 6 7 |
def power_logarithmic(b, n): if n == 0: return 1 elif n % 2 == 0: return power_logarithmic(b, n // 2) ** 2 else: return b * (power_logarithmic(b, (n - 1) // 2) ** 2) |
Время выполнения можно определить как:
1, если n = 0 T(n) = T(n / 2) + 1, если n > 0
1 2 3 4 5 6 7 8 |
def power_alt(b, n): if n == 0: return 1 elif n % 2 == 0: return power_alt(b, n // 2) * power_alt(b, n // 2) else: return (power_alt(b, (n - 1) // 2) * power_alt(b, (n - 1) // 2) * b) |
Временная стоимость выполнения этой функции:
1, если n = 0 T(n) = 2T(n / 2) + 1, если n > 0
На следующем шаге мы рассмотрим медленное сложение.