LoginSignup
1
2

More than 3 years have passed since last update.

立ちあがっているビットを数え上げるアルゴリズム

Posted at

この記事は何か?

立ちあがっているビットを数え上げるアルゴリズムについて取り上げる。

たとえば十進数で 7 という数は Python では 0b111 と表現できる。 0b は単に数が二進数であることを表現しているだけなので、数値表現部分は 111 である。つまり、立ちあがっているビットは 3 つである。このように、数を十進数で与えられたときに、それを二進数で表現した場合に立ちあがっているビット数を返す方法を探求したい。

最下位ビットからナイーブに数え上げる方法

次のような方法がもっともナイーブだろう。

def count_bits(n):
    count = 0
    while n:
        if n & 1 == 1:
            count += 1
        n >>= 1

    return count

なんのことはない。数を 1 ビットずつ右にシフトしながら、最下位ビットと 1 の AND が 1 になるものを数えているだけである。

立ち上がっているビットを右から落としていく方法

x = x & (x - 1) で x の最下位ビットを落とせる。したがって x0 になるまで x = x & (x - 1) を繰り返すことで、立ち上がっているビットを数えられる。

具体的には次のように書ける。

def count_bits(n):
    count = 0
    while n:
        count += 1
        n = n & (n - 1)

    return count

数と立ち上がっているビット数のマップをあらかじめ作っておく方法

あらかじめ Python のリストに「立ち上がっているビット数」を計算してつめておく方法も考えられる。 CPython のリストは C 言語 での配列で実装されているため、この方法であれば与えられた数を二進数表現した際に立ち上がっているビットの数を O(1) で返せる。

仮に 64 ビットの整数が入力される場合、この方法を単純に適用するとリストが大きくなりすぎるため現実的ではない。そういうときは全体のビット列を n 分割するとよい。入力が 64 ビットの整数である場合、16 ビットごとに分割して考えると、2^16 (すなわち 65536) 個の要素を持つリストさえあればよい。

具体的には、まず次のように 16 ビットの整数に対応するリストを作成する。

def _count_bits(n):
    count = 0
    while n:
        count += 1
        n = n & (n - 1)

    return count


PRE_CALCULATED_COUNT = []
for i in range(65536):  # 65536 = 2^16
    PRE_CALCULATED_COUNT.append(_count_bits(i))

ここで計算した PRE_CALCULATED_COUNT を使い、ビットマスクを使いながら 16 ビットずつ立ち上がりビット数を調べて足し合わせればよい。

def count_bits(n):
    bitmask = 0xffff
    return (PRE_CALCULATED_COUNT[(n & bitmask)] +
            PRE_CALCULATED_COUNT[(n >> 16 & bitmask)] +
            PRE_CALCULATED_COUNT[(n >> (16 * 2) & bitmask)] +
            PRE_CALCULATED_COUNT[(n >> (16 * 3) & bitmask)])
1
2
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2