web-dev-qa-db-ja.com

与えられた値以上の最小の2のべき乗を見つけるアルゴリズム

与えられた値以上の最小の2のべき乗を見つける必要があります。これまでのところ、私はこれを持っています:

int value = 3221; // 3221 is just an example, could be any number
int result = 1;

while (result < value) result <<= 1;

正常に動作しますが、素朴な感じがします。その問題に適したアルゴリズムはありますか?

編集。 Nice Assemblerの提案がいくつかあったので、これらのタグを質問に追加します。

47
Boyan

これが私のお気に入りです。無効かどうかの最初のチェック(<0、> = 0の数値しか渡されないことがわかっている場合はスキップできます)以外は、ループや条件がなく、他のほとんどのメソッドよりも優れています。これはエリクソンの答えと似ていますが、最初にxをデクリメントして最後に1を加えると、彼の答えよりも少し厄介であると思います(また、最後の条件を回避します)。

/// Round up to next higher power of 2 (return x if it's already a power
/// of 2).
inline int
pow2roundup (int x)
{
    if (x < 0)
        return 0;
    --x;
    x |= x >> 1;
    x |= x >> 2;
    x |= x >> 4;
    x |= x >> 8;
    x |= x >> 16;
    return x+1;
}
62
Larry Gritz
_ceil(log2(value))
_

ilog2()は、3つのasm命令で計算できます。例: http://www.asterisk.org/doxygen/1.4/log2comp_8h-source.html

19
jfs

Quake IIの0x5f3759dfとBit Twiddling HacksのIEEEバージョンの精神に則って-このソリューションはdoubleに達し、floor(lg2(n))を計算する手段として指数を抽出します。浮動小数点演算を回避するため、一般的なソリューションよりも少し高速で、Bit Twiddling IEEEバージョンよりもはるかに高速です。コード化されているように、リトルエンディアンマシンでdoubleが実数* 8のIEEE浮動小数点数であると想定しています。

int nextPow2(int n) 
{ 
    if ( n <= 1 ) return n;
    double d = n-1; 
    return 1 << ((((int*)&d)[1]>>20)-1022); 
} 

編集:同僚の助けを借りて、最適化されたx86アセンブリバージョンを追加します。速度は4%向上しますが、BSRバージョンよりも約50%遅くなります(私のラップトップではn = 1..2 ^ 31-2の4秒に対して6秒)。

int nextPow2(int n) 
{ 
    if ( n <= 1 ) return n;
    double d;
    n--;
    __asm {
      fild    n 
      mov     eax,4
      fstp    d 
      mov     ecx, dword ptr d[eax]
      sar     ecx,14h 
      rol     eax,cl 
  }
} 
11
Tony Lee

Intelハードウェアでは、BSR命令は必要なものに近く、最も重要なセットビットが検出されます。より正確にする必要がある場合は、残りのビットが正確にゼロであるかどうかを確認できます。他のCPUにはBSRのようなものがあると思いがちですが、これは、数値を正規化するために回答したい質問です。数値が32ビットを超える場合は、最上位のDWORDからスキャンして、[〜#〜] any [〜#〜]ビットが設定された最初のDWORDを見つけます。 Edsger Dijkstraは、上記の「アルゴリズム」はコンピューターが2進数字を使用していると想定していると述べている可能性があります。

9
pngaz

あなたの実装は素朴ではありません、それが間違っていることを除いて、それは実際には論理的なものです-それは最大整数サイズの1/2より大きい数に対して負を返します。

数値を0から2 ^ 30の範囲(32ビット整数の場合)に制限できると仮定すると、それはうまく機能し、対数を含む数学関数よりもはるかに高速に動作します。

符号なし整数はより適切に機能しますが、<<演算子では2 ^ 32に到達できないため、無限ループ(2 ^ 31より大きい数値の場合)が発生します。

6
paxdiablo

pow(2、ceil(log2(value));

log2(値)= log(値)/ log(2);

5
Sorana

密接に関連する問題(つまり、切り上げではなく切り捨て)の可能な解決策の探索は、その多くが単純なアプローチよりもはるかに高速であり、 Bit Twiddling Hacks ページで入手できます。探している種類の最適化を行うためのリソース。最速の解決策は、256エントリのルックアップテーブルを使用することです。これにより、単純なアプローチの場合、合計操作数は平均62(平均操作数は62)から約7に減少します。これらのソリューションを問題に適応させることは、1つの比較と増分の問題です。

4
Sparr

これは、ビットシフトテクニックのテンプレートバージョンです。

template<typename T> T next_power2(T value)
{
    --value;
    for(size_t i = 1; i < sizeof(T) * CHAR_BIT; i*=2)
        value |= value >> i;
    return value+1;
}

ループは定数のみを使用するため、コンパイラーによってフラット化されます。 (確認しました)機能も将来の保証です。

以下は__builtin_clzを使用するものです。 (また将来の証拠)

template<typename T> T next_power2(T value)
{
    return 1 << ((sizeof(T) * CHAR_BIT) - __builtin_clz(value-1));
}
4
Zacrath

コンパイル定数を生成するための再帰的なテンプレートバージョンはどうでしょうか。

template<uint32_t A, uint8_t B = 16>
struct Pow2RoundDown { enum{ value = Pow2RoundDown<(A | (A >> B)), B/2>::value }; };
template<uint32_t A>
struct Pow2RoundDown<A, 1> { enum{ value = (A | (A >> 1)) - ((A | (A >> 1)) >> 1) }; };

template<uint32_t A, uint8_t B = 16>
struct Pow2RoundUp { enum{ value = Pow2RoundUp<((B == 16 ? (A-1) : A) | ((B == 16 ? (A-1) : A) >> B)), B/2>::value }; };
template<uint32_t A >
struct Pow2RoundUp<A, 1> { enum{ value = ((A | (A >> 1)) + 1) }; };

次のように使用できます:

Pow2RoundDown<3221>::value, Pow2RoundUp<3221>::value
3
duncan.forster

「より良いアルゴリズム」が何を意味するのか実際に言うことはありませんが、提示するアルゴリズムは完全に明確であるため(多少欠陥がある場合)、より効率的なアルゴリズムを求めていると思います。

Larry Gritzは、ルックアップテーブルのオーバーヘッドなしでおそらく最も効率的なc/c ++アルゴリズムを提供しており、ほとんどの場合それで十分です(同様のアルゴリズムについては http://www.hackersdelight.org を参照してください) )。

他の場所で述べたように、最近のほとんどのCPUには先行ゼロの数をカウントする(または同等にmsセットビットを返す)機械命令がありますが、その使用は移植不可能であり、ほとんどの場合、努力する価値はありません。

ただし、ほとんどのコンパイラには、機械命令の使用を可能にする「移植性の高い」関数がありますが、移植性は高くなっています。

Microsoft C++には_BitScanReverse()があり、gccは大量の作業を効率的に行う__builtin_clz()を提供します。

3
Dipstick

同じの私のバージョン:

int pwr2Test(size_t x) {
    return (x & (x - 1))? 0 : 1; 
}

size_t pwr2Floor(size_t x) {
    // A lookup table for rounding up 4 bit numbers to
    // the nearest power of 2.
    static const unsigned char pwr2lut[] = {
        0x00, 0x01, 0x02, 0x02,     //  0,  1,  2,  3
        0x04, 0x04, 0x04, 0x04,     //  4,  5,  6,  7
        0x08, 0x08, 0x08, 0x08,     //  8,  9, 10, 11
        0x08, 0x08, 0x08, 0x08      // 12, 13, 14, 15
    };

    size_t pwr2 = 0;                // The return value
    unsigned int i = 0;             // The nybble interator

    for( i = 0; x != 0; ++i ) {     // Iterate through nybbles
        pwr2 = pwr2lut[x & 0x0f];   // rounding up to powers of 2.
        x >>= 4;                    // (i - 1) will contain the
    }                               // highest non-zero nybble index.

    i = i? (i - 1) : i;
    pwr2 <<= (i * 4);
    return pwr2; 
}

size_t pwr2Size(size_t x) {
    if( pwr2Test(x) ) { return x; }
    return pwr2Floor(x) * 2; 
 }
1
natersoz

私はこれが反対票であると知っていますが、数値が十分に小さい場合(8ビットまたは16ビットなど)、直接ルックアップが最も高速になる可能性があります。

// fill in the table
unsigned short tab[65536];
unsigned short bit = tab[i];

最初に上位ワードを実行し、次に下位ワードを実行することで、32ビットに拡張できる可能性があります。

//
unsigned long bitHigh = ((unsigned long)tab[(unsigned short)(i >> 16)]) << 16;
unsigned long bitLow = 0;
if (bitHigh == 0){
    bitLow = tab[(unsigned short)(i & 0xffff)];
}
unsigned long answer = bitHigh | bitLow;

おそらく、shift-orメソッドよりも優れていますが、Wordのサイズを大きくすることもできます。

(実際には、これにより最高の1ビットが得られます。次に高い2の累乗を得るには、左に1シフトする必要があります。)

1
Mike Dunlavey

以下のコードは、数値が2の累乗になるまで最下位ビットを繰り返し除去し、最初に数値が2の累乗でない限り、結果を2倍にします。セットされたビット数に比例した時間で実行できるという利点があります。残念ながら、ほとんどの場合、問題のコードやアセンブリの提案よりも多くの命令が必要になるという欠点があります。完全を期すためにのみ含めています。

int nextPow(int x) {
  int y = x
  while (x &= (x^(~x+1))) 
    y = x << 1;
  return y
}
1
DocMax

私はシフトが大好きです。

私は落ち着きます

    int bufferPow = 1;
    while ( bufferPow<bufferSize && bufferPow>0) bufferPow <<= 1;

このようにして、ループは常に終了し、&&の後の部分はほとんど評価されません。そして、私は2行が関数呼び出しの価値があるとは思いません。また、あなたの判断に応じて、長くしたり短くしたりすることができ、それは非常に読みやすいです。 (bufferPowが負になった場合、うまくいけば、メインコードが速く終了します。)

通常、アルゴリズムの開始時に一度だけ2のべき乗を計算するため、最適化はとにかくばかげています。ただし、十分に退屈している人がスピードコンテストに関心を持っている場合は、上記の例と255 256 257 .. 4195 4196 4197を使用すると興味があります。

0
Kos Petoussis

任意の対数関数は、2の対数で割ることにより、2を底とする対数に変換できます。

$ /usr/local/pypy-1.9/bin/pypy
Python 2.7.2 (341e1e3821ff, Jun 07 2012, 15:38:48)
[PyPy 1.9.0 with GCC 4.4.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
And now for something completely different: ``<arigato> yes but there is not
much sense if I explain all about today's greatest idea if tomorrow it's
completely outdated''
>>>> import math
>>>> print math.log(65535)/math.log(2)
15.9999779861
>>>> print math.log(65536)/math.log(2)
16.0
>>>>

もちろん、浮動小数点演算が含まれるため、100%正確ではありません。

0
user1277476