Вычислить быстрый бревенчатый фундамент 2 потолка
Что такое быстрый способ вычисления (long int) ceiling(log_2(i))
, где ввод и вывод являются 64-битными целыми числами? Решения для подписанных или беззнаковых целых чисел приемлемы. Я подозреваю, что наилучшим способом будет метод бит-twiddling, подобный найденному здесь, но вместо того, чтобы пытаться самостоятельно, я хотел бы использовать что-то, что уже хорошо протестирован. Общее решение будет работать для всех положительных значений.
Например, значения для 2,3,4,5,6,7,8 составляют 1,2,2,3,3,3,3
Изменить: пока лучший маршрут, по-видимому, состоит в том, чтобы вычислить базовую базу 2-го числа/пол (позицию MSB) с использованием любого количества быстрых существующих битаков или методов регистрации, а затем добавить один, если вход не является сила двух. Быстрая побитовая проверка для степеней двух составляет (n&(n-1))
.
Редактировать 2: Хороший источник для целочисленных логарифмов и ведущих методов нулей - это разделы 5-3 и 11-4 в Hacker Delight от Henry S Уоррен. Это наиболее полное лечение, которое я нашел.
Ответы
Ответ 1
Этот алгоритм уже опубликован, но следующая реализация очень компактна и должна оптимизировать код без ветвей.
int ceil_log2(unsigned long long x)
{
static const unsigned long long t[6] = {
0xFFFFFFFF00000000ull,
0x00000000FFFF0000ull,
0x000000000000FF00ull,
0x00000000000000F0ull,
0x000000000000000Cull,
0x0000000000000002ull
};
int y = (((x & (x - 1)) == 0) ? 0 : 1);
int j = 32;
int i;
for (i = 0; i < 6; i++) {
int k = (((x & t[i]) == 0) ? 0 : j);
y += k;
x >>= k;
j >>= 1;
}
return y;
}
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
printf("%d\n", ceil_log2(atol(argv[1])));
return 0;
}
Ответ 2
Если вы можете ограничить себя gcc, есть набор встроенных функций, которые возвращают число ведущих нулевых битов и могут использоваться для выполнения того, что вы хотите, с небольшой работой:
int __builtin_clz (unsigned int x)
int __builtin_clzl (unsigned long)
int __builtin_clzll (unsigned long long)
Ответ 3
Если вы компилируете для 64-разрядных процессоров в Windows, я думаю, что это должно сработать. _BitScanReverse64 - это внутренняя функция.
#include <intrin.h>
__int64 log2ceil( __int64 x )
{
unsigned long index;
if ( !_BitScanReverse64( &index, x ) )
return -1LL; //dummy return value for x==0
// add 1 if x is NOT a power of 2 (to do the ceil)
return index + (x&(x-1)?1:0);
}
Для 32-разрядных, вы можете эмулировать _BitScanReverse64, с 1 или 2 вызовами _BitScanReverse.
Проверьте верхние 32 бита x, ((long *) & x) [1], затем нижние 32-биты, если необходимо, ((long *) & x) [0].
Ответ 4
#include "stdafx.h"
#include "assert.h"
int getpos(unsigned __int64 value)
{
if (!value)
{
return -1; // no bits set
}
int pos = 0;
if (value & (value - 1ULL))
{
pos = 1;
}
if (value & 0xFFFFFFFF00000000ULL)
{
pos += 32;
value = value >> 32;
}
if (value & 0x00000000FFFF0000ULL)
{
pos += 16;
value = value >> 16;
}
if (value & 0x000000000000FF00ULL)
{
pos += 8;
value = value >> 8;
}
if (value & 0x00000000000000F0ULL)
{
pos += 4;
value = value >> 4;
}
if (value & 0x000000000000000CULL)
{
pos += 2;
value = value >> 2;
}
if (value & 0x0000000000000002ULL)
{
pos += 1;
value = value >> 1;
}
return pos;
}
int _tmain(int argc, _TCHAR* argv[])
{
assert(getpos(0ULL) == -1); // None bits set, return -1.
assert(getpos(1ULL) == 0);
assert(getpos(2ULL) == 1);
assert(getpos(3ULL) == 2);
assert(getpos(4ULL) == 2);
for (int k = 0; k < 64; ++k)
{
int pos = getpos(1ULL << k);
assert(pos == k);
}
for (int k = 0; k < 64; ++k)
{
int pos = getpos( (1ULL << k) - 1);
assert(pos == (k < 2 ? k - 1 : k) );
}
for (int k = 0; k < 64; ++k)
{
int pos = getpos( (1ULL << k) | 1);
assert(pos == (k < 1 ? k : k + 1) );
}
for (int k = 0; k < 64; ++k)
{
int pos = getpos( (1ULL << k) + 1);
assert(pos == k + 1);
}
return 0;
}
Ответ 5
Истинное быстрое решение:
Двоичное дерево поиска из 63 записей. Это полномочия 2 от 0 до 63. Одноразовая функция генерации для создания дерева. Листы представляют лог-базу 2 степеней (в основном, числа 1-63).
Чтобы найти ответ, вы подаете число в дерево и переходите к листу node больше, чем элемент. Если лист node точно равен, результат будет значением листа. В противном случае результатом будет значение листа + 1.
Сложность фиксируется в точке O (6).
Ответ 6
Используя встроенные функции gcc, упомянутые в @egosys, вы можете создать несколько полезных макросов.
Для быстрого и грубого расчета (log2 (x)) вы можете использовать:
#define FAST_LOG2(x) (sizeof(unsigned long)*8 - 1 - __builtin_clzl((unsigned long)(x)))
Для аналогичного ceil (log2 (x)) используйте:
#define FAST_LOG2_UP(x) (((x) - (1 << FAST_LOG2(x))) ? FAST_LOG2(x) + 1 : FAST_LOG2(x))
Последнее может быть дополнительно оптимизировано с использованием более gcc-особенностей, чтобы избежать двойного вызова встроенного устройства, но я не уверен, что вам это нужно здесь.
Ответ 7
Следующий фрагмент кода является безопасным и переносимым способом расширения простых методов C, таких как @dgobbi, для использования встроенных компиляторов при компиляции с использованием поддерживающих компиляторов (Clang). Размещение этого метода в верхней части метода приведет к тому, что метод будет использовать встроенный, когда он будет доступен. Когда встроенный недоступен, метод вернется к стандартному коду C.
#ifndef __has_builtin
#define __has_builtin(x) 0
#endif
#if __has_builtin(__builtin_clzll) //use compiler if possible
return ((sizeof(unsigned long long) * 8 - 1) - __builtin_clzll(x)) + (!!(x & (x - 1)));
#endif
Ответ 8
Поиск базы данных 2 целочисленного (64-разрядного или любого другого) с целым выходом эквивалентен поиску наиболее значимого бита, который установлен. Зачем? Поскольку база базы 2 состоит в том, сколько раз вы можете разделить число на 2, чтобы достичь 1.
Один из способов найти MSB, который установлен, - это просто битрейт справа на 1 каждый раз, пока вы не получите 0. Еще один эффективный способ - сделать какой-то бинарный поиск через битмаски.
Часть потолка легко разработана, проверяя, установлены ли какие-либо другие биты, отличные от MSB.
Ответ 9
Если вы имеете доступ к 80-битным или 128-битным поплавкам, отбрасываете этот тип и затем считываете биты экспоненты. Эта ссылка содержит сведения (для целых чисел до 52 бит) и несколько других методов:
http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogIEEE64Float
Также проверьте источник ffmpeg. Я знаю, что у них очень быстрый алгоритм. Даже если он не является прямым расширением для больших размеров, вы можете легко сделать что-то вроде if (x>INT32_MAX) return fastlog2(x>>32)+32; else return fastlog2(x);
Ответ 10
Наивный линейный поиск может быть опцией для равномерно распределенных целых чисел, так как для этого требуется немного меньше двух сравнений (для любого целочисленного размера).
/* between 1 and 64 comparisons, ~2 on average */
#define u64_size(c) ( \
0x8000000000000000 < (c) ? 64 \
: 0x4000000000000000 < (c) ? 63 \
: 0x2000000000000000 < (c) ? 62 \
: 0x1000000000000000 < (c) ? 61 \
...
: 0x0000000000000002 < (c) ? 2 \
: 0x0000000000000001 < (c) ? 1 \
: 0 \
)
Ответ 11
Код ниже проще и будет работать до тех пор, пока входной сигнал x >= 1. вход clog2 (0) получит ответ undefined (что имеет смысл, поскольку log (0) бесконечно...) Вы можете добавьте проверку ошибок (x == 0), если хотите:
unsigned int clog2 (unsigned int x)
{
unsigned int result = 0;
--x;
while (x > 0) {
++result;
x >>= 1;
}
return result;
}
Кстати, код для пола log2 схож: (опять же, предполагая x >= 1)
unsigned int flog2 (unsigned int x)
{
unsigned int result = 0;
while (x > 1) {
++result;
x >>= 1;
}
return result;
}