Вычислить быстрый бревенчатый фундамент 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;
}