Хабрахабр

Вычисление целочисленного квадратного корня

Возникла нужда проверить, является ли целое число квадратом, и если да, то вычислить корень. Причем хочется сделать это в целочисленной арифметике. Понятно, что можно реализовать метод Ньютона в целых числах, но он требует деления на каждом шаге. А нельзя ли по другому? Найти квадратный корень по модулю степени двойки, и проверить, а не будет ли он обычным квадратным корнем.
Можно ограничиться нечетными числами: для четного числа, если количество нулевых младших разрядов нечетно, то корня нет, а если четно, то можно сдвинуть число вправо, посчитать корень от нечетного, и сдвинуть обратно влево на половину от первоначального количества нулевых бит.

Нам нужен наименьший из этих четырех корней x. Для нечетного N и 2k, k > 3, если N ≡ 1 mod 8, то есть 4 разных корня по модулю 2k, а иначе корней нет. При этом другие три корня это 2k - x, 2k-1 + x и 2k - 2k-1 - x

Хочется что-то подобное вычислению обратного по модулю 2k — удваивая количество верных бит за итерацию.

Пусть у нас уже есть корень x0 из N по модулю 2k: N - x02 = 2ka
И мы хотим найти x1 = x0 + 2k-1y, такое чтобы в N - x12 было больше младших нулевых бит.
N - (x0 + 2k-1y)2 = 2ka - 2kx0 * y - 22k-2y2
Поделим на 2k: a - x0 * y - 2k-2y2
И приравняем к 0 по модулю 2k-2: y = a * x0-1 mod 2k-2
Получилии x1 = x0 + 2k-1a * (x0-1 mod 2k-2)
И окончательно x1 = x0 + (N - x02)/2 * (x0-1 mod 2k-2)

Параллельно считаем на каждой итерации обратное к корню. Из k бит на следующей итерации получится 2(k-1) бит.

Тестовый код:

uint8_t sqr16(uint16_t n) { if (n % 8 != 1) return 0; uint16_t sqr = (n + 1) / 2; //4 bit uint16_t inv = 2 - sqr; sqr += inv * (n-sqr*sqr)/2; //6 bit inv *= 2 - sqr * inv; sqr += inv * (n-sqr*sqr)/2; //10 bit //inv *= 2 - sqr * inv; if (sqr & 256) sqr = 0u - sqr; sqr = (uint8_t)sqr; // lowest root if (n == sqr*sqr) return sqr; return 0;
}

Добавив пару итераций, получим корень из uint_64

Показать больше

Похожие публикации

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»