Бинарное (двоичное) возведение в степень — это приём, позволяющий возводить любое число в n-ую степень за O(log n) умножений (вместо n умножений при обычном подходе).
Более того, описываемый здесь приём применим к любой ассоциативной операции, а не только к умножению чисел. Напомним, операция называется ассоциативной, если для любых a, b, c выполняется:
(a * b) * c = a * (b * c)
Наиболее очевидное обобщение — на остатки по некоторому модулю (очевидно, ассоциативность сохраняется). Следующим по "популярности" является обобщение на произведение матриц (его ассоциативность общеизвестна).

Алгоритм

Заметим, что для любого числа a и чётного числа n выполнимо очевидное тождество (следующее из ассоциативности операции умножения):
a^n = (a^{n/2})^2 = a^{n/2} * a^{n/2}
Оно и является основным в методе бинарного возведения в степень. Действительно, для чётного n мы показали, как, потратив всего одну операцию умножения, можно свести задачу к вдвое меньшей степени.
Осталось понять, что делать, если степень n нечётна. Здесь мы поступаем очень просто: перейдём к степени n-1, которая будет уже чётной:
a^n = a^{n-1} * a
Итак, мы фактически нашли рекуррентную формулу: от степени n мы переходим, если она чётна, к n/2, а иначе — к n-1. Понятно, что всего будет не более 2 log n переходов, прежде чем мы придём к n = 0 (базе рекуррентной формулы). Таким образом, мы получили алгоритм, работающий за O (log n) умножений.

Реализация

Простейшая рекурсивная реализация:
int binpow (int a, int n) {
	if (n == 0)
		return 1;
	if (n % 2 == 1)
		return binpow (a, n-1) * a;
	else {
		int b = binpow (a, n/2);
		return b * b;
	}
}
Нерекурсивная реализация, также оптимизированная (деления на 2 заменены битовыми операциями):
int binpow (int a, int n) {
	int res = 1;
	while (n)
		if (n & 1) {
			res *= a;
			--n;
		}
		else {
			a *= a;
			n >>= 1;
		}
	return res;
}
Эту реализацию можно ещё несколько упростить, заметив, что возведение a в квадрат осуществляется всегда, независимо от того, сработало условие нечётности n или нет:
int binpow (int a, int n) {
	int res = 1;
	while (n) {
		if (n & 1)
			res *= a;
		a *= a;
		n >>= 1;
	}
	return res;
}
`
ОЖИДАНИЕ РЕКЛАМЫ...
0
23
4 года назад
0
Интересный подход.
2
37
4 года назад
Отредактирован ScorpioT1000
2
Не любое число, а integer в степень unsigned integer *smh*

И подозреваю, что первый аргумент тоже должен быть unsigned
0
19
4 года назад
0
Я так понял, главный упор идёт на оптимизацию.
0
23
4 года назад
0
Ev3nt:
Я так понял, главный упор идёт на оптимизацию.
В этом весь смысл)
Чтобы быстрее работало.
Чтобы оставить комментарий, пожалуйста, войдите на сайт.