web-dev-qa-db-ja.com

1の数をバイナリ表現でカウントします

十分なメモリがあれば、O(1)の数値のバイナリ表現で1の数をカウントする効率的な方法。これはオンラインフォーラムで見つけたインタビューの質問ですが、答えはありませんでした。誰かが何かを提案できますか、O(1)時間内にそれを行う方法を考えることはできませんか?

70

それが Hamming weight 問題、別名人口カウントです。リンクには効率的な実装が記載されています。引用:

無制限のメモリを使用すると、64ビット整数ごとのハミング重みの大きなルックアップテーブルを簡単に作成できます。

54
Óscar López

O(Number of 1's)時間のビットをカウントするソリューションがあります:

bitcount(n):
    count = 0
    while n > 0:
        count = count + 1
        n = n & (n-1)
    return count

最悪の場合(数値が2 ^ n-1の場合、すべて1がバイナリの場合)、すべてのビットがチェックされます。

編集:ビットカウントのための非常に素晴らしい定数時間、定数メモリアルゴリズムを見つけました。これはCで書かれています。

int BitCount(unsigned int u)
{
     unsigned int uCount;

     uCount = u - ((u >> 1) & 033333333333) - ((u >> 2) & 011111111111);
     return ((uCount + (uCount >> 3)) & 030707070707) % 63;
}

その正確さの証拠を見つけることができます こちら

41
0605002

N&(n-1)は常に最下位の1を常に除去するという事実に注意してください。

したがって、次のように1の数を計算するコードを記述できます。

count=0;
while(n!=0){
  n = n&(n-1);
  count++;
}
cout<<"Number of 1's in n is: "<<count;

プログラムの複雑さは次のようになります:nの1の数(常に32未満)。

17
Sriram Mahavadi

私は別のウェブサイトから次の解決策を見ました:

int count_one(int x){
    x = (x & (0x55555555)) + ((x >> 1) & (0x55555555));
    x = (x & (0x33333333)) + ((x >> 2) & (0x33333333));
    x = (x & (0x0f0f0f0f)) + ((x >> 4) & (0x0f0f0f0f));
    x = (x & (0x00ff00ff)) + ((x >> 8) & (0x00ff00ff));
    x = (x & (0x0000ffff)) + ((x >> 16) & (0x0000ffff));
    return x;
}
14
user2555279
public static void main(String[] args) {

    int a = 3;
    int orig = a;
    int count = 0;
    while(a>0)
    {
        a = a >> 1 << 1;
        if(orig-a==1)
            count++;
        orig = a >> 1;
        a = orig;
    }

    System.out.println("Number of 1s are: "+count);
}
10
akus
   countBits(x){
     y=0;
     while(x){   
       y += x &  1 ;
       x  = x >> 1 ;
     }
   }

それでおしまい?

6
user40521

それは私のSO人生で最も短い答えになります:lookup table。

どうやら、私は少し説明する必要があります。「あなたが遊ぶのに十分なメモリがあるなら」ということは、私たちが必要とするすべてのメモリを持っていることを意味します(技術的な可能性を考えないでください)。これで、ルックアップテーブルを1〜2バイト以上保存する必要がなくなりました。技術的にはO(1)ではなくΩ(log(n))になりますが、必要な数値を読み取るだけでΩ(log(n))になります。したがって、それが問題であれば、答えは不可能 —これはさらに短い。

面接であなたから期待される2つの答えのうち、誰も知りません。

さらに別のトリックがあります:エンジニアは数値を取得してΩ(log(n))(nは数値)について話すことができますが、コンピューター科学者は実際に実行時間を関数length入力なので、エンジニアがΩ(log(n))と呼ぶものは実際にはΩ(k)です。ここで、kはバイト数です。それでも、前に言ったように、数値を読むだけでΩ(k)なので、それ以上に良い方法はありません。

3
alf

以下も同様に機能します。

nofone(int x) {
  a=0;
  while(x!=0) {
    x>>=1;
    if(x & 1)
      a++;
  }
  return a;
} 
2
Vigneswaran

Javascriptでそうするための最良の方法は

function getBinaryValue(num){
 return num.toString(2);
}

function checkOnces(binaryValue){
    return binaryValue.toString().replace(/0/g, "").length;
}

ここで、binaryValueはバイナリ文字列です。例:1100

1

以下は、これを行うことができる多くの中での2つの簡単な例(C++)です。

  1. __builtin_popcount()を使用して、設定ビット(1)を単純にカウントできます。

    int numOfOnes(int x) { return __builtin_popcount(x); }

  2. 整数内のすべてのビットをループし、ビットが設定されているかどうかを確認してから、カウント変数をインクリメントします。

    int hammingDistance(int x) { int count = 0 for(int i = 0; i < 32; i++) if(x & (1 << i)) count++; return count; }

お役に立てれば!

1
Gaurav Sharma

この関数はintを取り、バイナリ表現で1の数を返します

public static int findOnes(int number)
{

   if(number < 2)
    {
        if(number == 1)
        {
            count ++;
        }
        else
        {
            return 0;
        }
    }

    value = number % 2;

    if(number != 1 && value == 1)
        count ++;

    number /= 2;

    findOnes(number);

    return count;
}
1
Roshan

以下は、ビット演算子を使用したCソリューションです。

int numberOfOneBitsInInteger(int input) {
  int numOneBits = 0;

  int currNum = input;
  while (currNum != 0) {
    if ((currNum & 1) == 1) {
      numOneBits++;
    }
    currNum = currNum >> 1;
  }
  return numOneBits;
}

以下は、2の累乗を使用したJavaソリューションです。

public static int numOnesInBinary(int n) {

  if (n < 0) return -1;

  int j = 0;
  while ( n > Math.pow(2, j)) j++;

  int result = 0;
  for (int i=j; i >=0; i--){
    if (n >= Math.pow(2, i)) {
        n = (int) (n - Math.pow(2,i));
        result++;    
    }
  }

  return result;
}
1
eb80

Rubyの実装

def find_consecutive_1(n)
  num = n.to_s(2)
  arr = num.split("")
  counter = 0
  max = 0
  arr.each do |x|
      if x.to_i==1
          counter +=1
      else
          max = counter if counter > max
          counter = 0 
      end
      max = counter if counter > max  
  end
  max
end

puts find_consecutive_1(439)
0
Jagdish N

2つの方法::

/* Method-1 */
int count1s(long num)
{
    int tempCount = 0;

    while(num)
    {
        tempCount += (num & 1); //inc, based on right most bit checked
        num = num >> 1;         //right shift bit by 1
    }

    return tempCount;
}

/* Method-2 */
int count1s_(int num)
{
    int tempCount = 0;

    std::string strNum = std::bitset< 16 >( num ).to_string(); // string conversion
    cout << "strNum=" << strNum << endl;
    for(int i=0; i<strNum.size(); i++)
    {
        if('1' == strNum[i])
        {
            tempCount++;
        }
    }

    return tempCount;
}

/* Method-3 (algorithmically - boost string split could be used) */
1) split the binary string over '1'.
2) count = vector (containing splits) size - 1

使用法::

    int count = 0;

    count = count1s(0b00110011);
    cout << "count(0b00110011) = " << count << endl; //4

    count = count1s(0b01110110);
    cout << "count(0b01110110) = " << count << endl;  //5

    count = count1s(0b00000000);
    cout << "count(0b00000000) = " << count << endl;  //0

    count = count1s(0b11111111);
    cout << "count(0b11111111) = " << count << endl;  //8

    count = count1s_(0b1100);
    cout << "count(0b1100) = " << count << endl;  //2

    count = count1s_(0b11111111);
    cout << "count(0b11111111) = " << count << endl;  //8

    count = count1s_(0b0);
    cout << "count(0b0) = " << count << endl;  //0

    count = count1s_(0b1);
    cout << "count(0b1) = " << count << endl;  //1
0
parasrish

私はこの問題の美しい解決策を知っていると信じてここに来ました。 Cのコード:

    short numberOfOnes(unsigned int d) {
        short count = 0;

        for (; (d != 0); d &= (d - 1))
            ++count;

        return count;
    }

しかし、このトピックについて少し調査した後(他の回答を読んでください:))、より効率的な5つのアルゴリズムが見つかりました。ラブラブ!

このタスク専用に設計されたCPU命令もあります:popcnt。 ( この回答 に記載)

見つけることができる多くのアルゴリズムの説明とベンチマーク こちら

0
naXa

以下の方法でも、負の数の1の数をカウントできます。

private static int countBits(int number)    {
    int result = 0;
    while(number != 0)  {
        result += number & 1;
        number = number >>> 1;
    }
    return result;
}

ただし、-1のような数値は11111111111111111111111111111111111としてバイナリで表されるため、多くのシフトが必要になります。小さい負の数に対してそれほど多くのシフトを行いたくない場合、別の方法は次のようになります。

private static int countBits(int number)    {
    boolean negFlag = false;
    if(number < 0)  { 
        negFlag = true;
        number = ~number;
    }

    int result = 0;
    while(number != 0)  {
        result += number & 1;
        number = number >> 1;
    }
    return negFlag? (32-result): result;
}
0
Menezes Sousa

これをRubyでゴルフしなければならず、結局

l=->x{x.to_s(2).count ?1}

使用法 :

l[2**32-1] # returns 32

明らかに効率的ではありませんが、トリックを行います:)

0
hoang

JSの文字列操作を使用することにより、次のようにできます。

0b1111011.toString(2).split(/0|(?=.)/).length // returns 6

または

0b1111011.toString(2).replace("0","").length  // returns 6
0
Redu

私は実際に少し手の込んだ方法を使用してこれを実行しました。16エントリの単一のルックアップテーブルで十分であり、必要なのはバイナリrepをニブル(4ビットタプル)に分割することだけです。実際、複雑さはO(1)であり、必要な整数のサイズ(#ビット単位)に特化したC++テンプレートを作成しました。不定ではなく定数式にします。

(i&-i)LSの1ビットを返すだけで、整数がゼロになるまで毎回lsbitを除去するというループを使用できますが、これは古いパリティトリックです。

0
kai26873

O(1)でこのタスクを達成するために考えることができる唯一の方法があります...それは「チート」し、物理デバイスを使用することです(線形または並列プログラミングでも、制限はO(log(k))ここで、kは数値のバイト数を表します)。

ただし、各ビットを出力ラインに0/1の電圧で接続する物理デバイスを非常に簡単に想像できます。次に、O(1)の「合計」行の総電圧を電子的に読み取ることができます。いくつかの基本的な回路要素を使用してこの基本的なアイデアをよりエレガントにし、任意の形式の出力(バイナリエンコード出力など)を生成することは非常に簡単ですが、基本的なアイデアは同じで、電子回路は正しい出力を生成します固定時間の状態。

量子コンピューティングの可能性も考えられますが、それが許されれば、簡単な電子回路がより簡単な解決策だと思います。

0
Tim Gee

pythonまたはその他のビン文字列への変換で、 '0'で分割して0を取り除き、結合して長さを取得します。

len(''.join(str(bin(122011)).split('0')))-1
0
ben