Целочисленный квадратный корень
В теории чисел целочисленный квадратный корень (isqrt) из неотрицательного целого числа n — это неотрицательное целое число m, которое является наибольшим целым числом, меньшим или равным квадратному корню из n ,
Например,
Вступительное замечание
[ редактировать ]Позволять и быть неотрицательными целыми числами.
Алгоритмы вычисления ( десятичное представление ) работать вечно на каждом входе что не является идеальным квадратом . [примечание 1]
Алгоритмы, которые вычисляют не беги вечно . Тем не менее, они способны вычислять с любой желаемой точностью .
Выбирайте любой и вычислить .
Например (установка ):
Сравните результаты с
Оказывается, что умножение входных данных на дает точность k десятичных цифр. [примечание 2]
Чтобы вычислить (полное) десятичное представление , можно выполнить бесконечное число раз, увеличивая по фактору на каждом проходе.
Предположим, что в следующей программе ( ) процедура уже определено и — ради аргумента — что все переменные могут содержать целые числа неограниченной величины.
Затем напечатает все десятичное представление . [примечание 3]
// Print sqrt(y), without halting
void sqrtForever(unsigned int y)
{
unsigned int result = isqrt(y);
printf("%d.", result); // print result, followed by a decimal point
while (true) // repeat forever ...
{
y = y * 100; // theoretical example: overflow is ignored
result = isqrt(y);
printf("%d", result % 10); // print last digit of result
}
}
Вывод состоит в том, что алгоритмы, вычисляющие isqrt()
вычислительно эквивалентны алгоритмам, которые вычисляют sqrt()
. [1]
Основные алгоритмы
[ редактировать ]Целый квадратный корень из неотрицательного целого числа может быть определен как
Например, потому что .
Алгоритм с использованием линейного поиска
[ редактировать ]Следующие программы на языке C представляют собой простую реализацию.
// Integer square root
// (using linear search, ascending)
unsigned int isqrt(unsigned int y)
{
// initial underestimate, L <= isqrt(y)
unsigned int L = 0;
while ((L + 1) * (L + 1) <= y)
L = L + 1;
return L;
}
// Integer square root
// (using linear search, descending)
unsigned int isqrt(unsigned int y)
{
// initial overestimate, isqrt(y) <= R
unsigned int R = y;
while (R * R > y)
R = R - 1;
return R;
}
Линейный поиск с использованием сложения
[ редактировать ]В приведенной выше программе (линейный поиск по возрастанию) можно заменить умножение сложением, используя эквивалентность
// Integer square root
// (linear search, ascending) using addition
unsigned int isqrt(unsigned int y)
{
unsigned int L = 0;
unsigned int a = 1;
unsigned int d = 3;
while (a <= y)
{
a = a + d; // (a + 1) ^ 2
d = d + 2;
L = L + 1;
}
return L;
}
Алгоритм, использующий бинарный поиск
[ редактировать ]Линейный поиск последовательно проверяет каждое значение, пока не достигнет наименьшего. где .
Ускорение достигается за счет использования вместо этого двоичного поиска . Следующая C-программа является реализацией.
// Integer square root (using binary search)
unsigned int isqrt(unsigned int y)
{
unsigned int L = 0;
unsigned int M;
unsigned int R = y + 1;
while (L != R - 1)
{
M = (L + R) / 2;
if (M * M <= y)
L = M;
else
R = M;
}
return L;
}
Численный пример
Например, если вычислить используя бинарный поиск, можно получить последовательность
Это вычисление занимает 21 итерацию, тогда как линейный поиск (по возрастанию, начиная с ) нужно 1414 шагов.
Алгоритм с использованием метода Ньютона
[ редактировать ]Один из способов расчета и заключается в использовании метода Герона , который является частным случаем метода Ньютона , для нахождения решения уравнения , давая итерационную формулу
Последовательность сходится квадратично к как .
Критерий остановки
[ редактировать ]Можно доказать [ нужна ссылка ] что — максимально возможное число, для которого критерий остановки обеспечивает в алгоритме выше.
В реализациях, которые используют числовые форматы, которые не могут точно представлять все рациональные числа (например, с плавающей запятой), следует использовать останавливающую константу меньше 1 для защиты от ошибок округления.
Область вычислений
[ редактировать ]Хотя иррационально для многих , последовательность содержит только рациональные члены, когда является рациональным. Таким образом, при этом методе нет необходимости выходить из поля рациональных чисел, чтобы вычислить , факт, который имеет некоторые теоретические преимущества.
Использование только целочисленного деления
[ редактировать ]Для вычислений для очень больших целых чисел n можно использовать частное евклидова деления для обеих операций деления. Это имеет то преимущество, что для каждого промежуточного значения используются только целые числа, что делает ненужным использование с плавающей запятой представлений больших чисел . Это эквивалентно использованию итерационной формулы
Используя тот факт, что
можно показать, что это достигнет за конечное число итераций.
В оригинальной версии есть для , и для . Итак, в целочисленной версии имеем и до окончательного решения достигается. Для окончательного решения , у одного есть и , поэтому критерием остановки является .
Однако, не обязательно является фиксированной точкой приведенной выше итерационной формулы. Действительно, можно показать, что является фиксированной точкой тогда и только тогда, когда не является идеальным квадратом. Если является идеальным квадратом, последовательность завершается циклом второго периода между и вместо того, чтобы сходиться.
Пример реализации на C
[ редактировать ]// Square root of integer
unsigned int int_sqrt(unsigned int s)
{
// Zero yields zero
// One yields one
if (s <= 1)
return s;
// Initial estimate (must be too high)
unsigned int x0 = s / 2;
// Update
unsigned int x1 = (x0 + s / x0) / 2;
while (x1 < x0) // Bound check
{
x0 = x1;
x1 = (x0 + s / x0) / 2;
}
return x0;
}
Численный пример
[ редактировать ]Например, если вычислить целый квадратный корень из 2000000 , используя приведенный выше алгоритм, можно получить последовательность Всего необходимо 13 итераций. Хотя метод Герона сходится квадратично близко к решению, в начале достигается точность менее одного бита на итерацию. Это означает, что выбор начальной оценки имеет решающее значение для производительности алгоритма.
быстрое вычисление целой части двоичного логарифма или длины бита (например, Когда доступно std::bit_width
в C++20 ), лучше начать с
что является наименьшей степенью двух больше, чем . В примере целочисленного квадратного корня из 2000000 , , , и результирующая последовательность
В этом случае необходимо всего четыре шага итерации.
Поразрядный алгоритм
[ редактировать ]Традиционный с помощью ручки и бумаги. алгоритм вычисления квадратного корня основан на работе от более высоких цифр к меньшим, и по мере того, как каждая новая цифра выбирает самую большую, которая по-прежнему дает квадрат . Если остановиться после единицы, вычисленный результат будет целым квадратным корнем.
Использование побитовых операций
[ редактировать ]При работе с базой 2 выбор цифры упрощается до выбора между 0 («маленький кандидат») и 1 («большой кандидат»), а манипуляции с цифрами могут быть выражены с помощью операций двоичного сдвига. С *
будучи умножением, <<
будучи левой сменой, и >>
будучи логическим сдвигом вправо, рекурсивный алгоритм нахождения целого квадратного корня из любого натурального числа :
def integer_sqrt(n: int) -> int:
assert n >= 0, "sqrt works for only non-negative inputs"
if n < 2:
return n
# Recursive call:
small_cand = integer_sqrt(n >> 2) << 1
large_cand = small_cand + 1
if large_cand * large_cand > n:
return small_cand
else:
return large_cand
# equivalently:
def integer_sqrt_iter(n: int) -> int:
assert n >= 0, "sqrt works for only non-negative inputs"
if n < 2:
return n
# Find the shift amount. See also [[find first set]],
# shift = ceil(log2(n) * 0.5) * 2 = ceil(ffs(n) * 0.5) * 2
shift = 2
while (n >> shift) != 0:
shift += 2
# Unroll the bit-setting loop.
result = 0
while shift >= 0:
result = result << 1
large_cand = (
result + 1
) # Same as result ^ 1 (xor), because the last bit is always 0.
if large_cand * large_cand <= n >> shift:
result = large_cand
shift -= 2
return result
Традиционные описания поразрядного алгоритма на бумаге включают в себя различные оптимизации, отсутствующие в приведенном выше коде, в частности, трюк предварительного вычитания квадрата предыдущих цифр, что делает ненужным общий шаг умножения. Пример см . в разделе «Методы вычисления квадратных корней» § Двоичная система счисления (основание 2) . [2]
В языках программирования
[ редактировать ]Некоторые языки программирования в дополнение к общему случаю выделяют явную операцию для вычисления квадратного корня целого числа или могут быть расширены с этой целью библиотеками.
(isqrt x)
: Общий Лисп . [3]math.isqrt(x)
: Питон . [4]
См. также
[ редактировать ]Примечания
[ редактировать ]- ^ Квадратные корни идеальных квадратов (например, 0, 1, 4, 9, 16) являются целыми числами. Во всех остальных случаях квадратные корни натуральных чисел являются иррациональными числами.
- ^ Неудивительно, что повторное умножение на 100 является особенностью Джарвиса (2006).
- ^ Дробная часть квадратных корней из полных квадратов отображается как 000... .
Ссылки
[ редактировать ]- ^ см. « Методы вычисления квадратных корней ».
- ^ Ву, К. (июнь 1985 г.). «Квадратный корень по алгоритму счетов (в архиве)» . Архивировано из оригинала 06 марта 2012 г.
- ^ ЛиспВоркс Лтд. (1996). «CLHS: Функция SQRT, ISQRT» . www.lispworks.com .
- ^ Фонд программного обеспечения Python (2001). «Математические функции» . Документация стандартной библиотеки Python . (начиная с версии 3.8).
Внешние ссылки
[ редактировать ]- Джарвис, Эшли Фрейзер (2006). «Квадратные корни путем вычитания» (PDF) . Математический спектр . 37 : 119–122.
- Мински, Марвин (1967). «9. Вычислимые действительные числа». Вычисления: конечные и бесконечные машины . Прентис-Холл. ISBN 0-13-165563-9 . OCLC 0131655639 .
- «Геометрический взгляд на алгоритм извлечения квадратного корня» .