Быстрое возведение в степень
Пусть у нас есть какая-то ассоциативная бинарная операция в алгебре . Мы хотим научиться быстро считать степень элемента носителя алгебры по этой операции, то есть значение
В псевдокоде я заменю операцию на умножение, но это просто для удобства написания. В формулах я буду использовать оригинальное обозначение, дабы подчеркнуть универсальность.
Можно применить свойство ассоциативности для сведения задачи возведения в степень к вдвое меньшей задаче, используя простое свойство
Из этого простого свойства можно сразу вывести простой рекурсивный алгоритм
function fast_pow(a, int n):
if n % 2 == 0:
return fast_pow(a, n/2) ** 2
else:
return a * fast_pow(a, n-1)У рекурсии есть оверхед, от которого хотелось бы избавиться.
Давайте посмотрим на двоичное представление
Тогда нашу степень можно переписать как
Ну и здесь сразу видно, как производить вычисления
function fast_pow(a, int n):
int result = 1
while n > 0:
if n % 2 == 1:
result *= a
a *= a
n /= 2;
return resultВажное замечание: в этом алгоритме — нейтральный элемент по операции .
Оценим количество умножений, требуемых алгоритмом (не важно каким, они одинаковые). Пусть для возведения числа в степень требуется умножений. Тогда, исходя из нашего алгоритма, справедлива формула
Решение этого уравнения
где — количество единиц в числе
Предподсчёт
Пусть у нас есть фиксированный объект . К нам поступает запросов возведения в степень ( - номер запроса).
Только что мы научились решать эту задачу за умножений, что в итоге выливается в временную сложность