Каков самый быстрый способ найти целочисленный квадратный корень, используя битовые сдвиги?

#math #assembly #square-root #dlx

#битовый сдвиг #экономия памяти #квадратный корень

Вопрос:

Я искал самый быстрый метод вычисления квадратного корня (целого числа) из числа (целого числа). Я наткнулся на это решение в википедии, которое находит квадратный корень из числа (если оно является идеальным квадратом) или квадратный корень из ближайшего нижнего идеального квадрата (если данное число не является идеальным квадратом:

 short isqrt(short num) {
    short res = 0;
    short bit = 1 << 14; // The second-to-top bit is set: 1L<<30 for long
    // "bit" starts at the highest power of four <= the argument.
    while (bit > num)
        bit >>= 2;
    while (bit != 0) {
        if (num >= res   bit) {
            num -= res   bit;
            res = (res >> 1)   bit;
        }
        else
            res >>= 1;
        bit >>= 2;
    }
    return res;
}
 

Я перепробовал много тестовых запусков, чтобы отследить алгоритм, но, похоже, я не понимаю часть внутри while(bit!=0) . Кто-нибудь может объяснить мне эту часть?

Ответ №1:

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

Пусть «num_init» будет значением num в начале функции. Предположим, что на некоторой итерации у нас есть бит = 4 ^ x и это число равно некоторому значению «num_curr» (беглый взгляд показывает, что пока бит не равен 0, он всегда равен степени 4). Тогда res имеет вид y * 2 ^(x 1), где y ^ 2 num_curr = num_init, а y меньше фактического ответа, но в пределах 2 ^ x .

Этот инвариант к значениям num, res и bit будет ключевым. Способ, которым это делается в коде, заключается в том, что

 while (bit != 0) {
    ....
}
 

перемещает наш воображаемый указатель слева направо, и на каждом шаге мы определяем, равен ли этот бит 0 или 1.

Переходя к первому оператору if, предположим, что наше воображаемое «встроенное» целое число равно y, и мы смотрим на бит 2 ^ x. Тогда бит равен 1, если исходное значение num равно как минимум (y 2 ^ x) ^ 2 = y ^ 2 y * 2 ^(x 1) 4 ^ x . Другими словами, бит равен единице, если значение num в этой точке равно как минимум y * 2 ^(x 1) 4 ^ x (поскольку у нас есть инвариант, что значение num уменьшилось на y ^ 2). Достаточно удобно, res = y * 2 ^(x 1) и bit = 4 ^ x . Затем мы получаем точку позади

 if (num >= res   bit) {
    num -= res   bit;
    res = (res >> 1)   bit;
}
else 
    res >>= 1;   
 

который при необходимости добавляет 1 бит в наше воображаемое место, а затем обновляет res и num, чтобы сохранить инвариант. Наконец

 bit >>= 2;
 

обновляет бит и перемещает все на один шаг.

Ответ №2:

Я использую его, чтобы получить квадратный корень из числа uint_fast64_t в C :

 #include <stdint.h>

static inline uint_fast64_t square_root(uint_fast64_t n) {
    uint_fast64_t a = 0, b, c;
    for (b = 1ULL << 62; b;)
        c = a   b, n -= c amp;= -(n >= c), a = (a >> 1) | (c amp; b), b >>= 2;
    return a;
}