В своих бесконечный поисках откопал на Википедии совсем не то, что искал, а именно — красивый алгоритм извлечения квадратного корня с остатком из длинных чисел. В вики как всегда алгоритм перепечатали из источника с ошибкой, и без всяких дополнительных разъяснений. Хорошо хоть ссылку на
оригинальную статью привели. Автор алгоритма, судя по статье, некий
Поль Зиммерманн, и почему-то он назвал свой алгоритм "
Квадратный корень Карацубы". Но да ладно.
Процедура алгоритма представляет собой следующее.
Вход: одно число
n.
Выход: два числа
s и
r, такие что
.
Шаги алгоритма:1) если число
n достаточно мало (влезает в 32 бита, например), то вычисляем величины
стандартными средствами и возвращаем результат.
2) представляем исходное число в виде
3) рекурсивно вызывая процедуру находим значения
s' и
r' для числа
(первая половина исходного числа)
4) находим частное
q и остаток
u от деления числа
на число
5) находим целую часть корня и остаток:
6) если остаток получился отрицательным (
), то делаем коррекцию:
7) возвращаем результат.
Замечания. Очевидно, что в качестве
b при вычислении на компьютере удобно брать степени двойки (или десятки, если работа идёт с числами в десятичном представлении). Не смотря на то, что в оригинале процедура расчёта рекурсивная, её можно свести к обычному циклу. Этому способствует то, что в теле процедуры есть только один самовызов. Так что остаётся только правильно подобрать последовательность чисел
b, чтобы идти от малых корней к большим, а не на оборот, как в рекурсии. Автор, видимо, вдохновлялся алгоритмом Карацубы с его "разделяй и властвуй". Но конкретно в случае вычисления корня "властвовать" нужно только над одной половинкой, так что нет необходимости городить рекурсию.
Алгоритм очень сильно перекликается с
методом Ньютона расчёта квадратного корня. Например, он так же удваивает число правильных разрядов с каждой "итерацией" (если идти из "дна" рекурсии "наружу"). А так же они оба используют деление больших чисел, что в обоих случаях является самой трудоёмкой операцией и определяет сложность алгоритма. Однако,
алгоритм Зиммерманна на каждой итерации выполняет деление не полноразрядных чисел, как метод Ньютона, а чисел экспоненциально убывающей длины (если идти снаружи в глубь рекурсии). Это даёт значительно более высокую производительность. Кроме того, за счёт правильного разбиения и за счёт подсчёта на каждом шаге ровно стольк
и бит, сколько требуется, алгоритм не имеет никаких проблем с зацикливанием или "переработкой", как метод Ньютона (когда, например, для окончания расчёта не хватило 1-2 верных бит, а следующая итерация "удваивает точность", давая очередные 200 или 200'000 бит).
В качестве иллюстрации можно посчитать целочисленный корень из числа
.
Сначала разбиение:
(
)
(
)
(
)
(
)
Корень из
равен
, остаток
Делим
на
, получаем
, остаток
Корень из
равен
, остаток
Делим
на
, получаем
, остаток
Корень из
равен
, остаток
Делим
на
, получаем
, остаток
Корень из
равен
, остаток
Делим
на
, получаем
, остаток
Корень из
равен
, остаток
Окончательный результат:
. Чудесным образом нигде не потребовалась коррекция.