ソードはオファーを指します (C++)-JZ15: バイナリの 1 の数 (アルゴリズムビット演算)

著者: Zhai Tianbao Steven
著作権表示: 著作権は著者に属します。商業的転載の場合は、許可について著者に連絡してください。非商業的な転載の場合は、出典を示してください。

タイトル説明:

整数 n を入力し、その数値の 32 ビット バイナリ表現で 1 の数を出力します。負の数は 2 の補数で表現されます。

データ範囲: −2^31<=n<=2^31−1

つまり、範囲は -2147483648<=n<=2147483647 です。

例:

入力:

10

戻り値:

2

例証します:

10 を 10 進数で表す 32 ビットの 2 進表現は、2 つの 1 を含む 0000 0000 0000 0000 0000 0000 0000 1010 です。

問題解決のアイデア:

この問題はビット演算について調べます。問題を解決する 3 つの方法。

1) 循環ビット単位比較

       暴力的なサイクルの場合は、各ビットの 1 を数えるだけで十分です。

2) ビット演算式 n&(n-1)

       ビット演算式 n&(n-1) は数値の右端の 1 を 0 に変えることができます。たとえば、1011 から 1 を引いた 1010 と AND 演算で 1010 が得られる例や、1010 から 1 を引いた後の 1001 と AND 演算で 1000 が得られる例があります。

       このプロパティを使用してループを実行します。n が 0 の場合、ループを終了してカウントを停止します。

3) 分割統治

       32 ビット数値 A を例として、1111 1111 1111 1111 1111 1111 1111 1111 を考えます。問題を解決するために分割統治の考え方を使用して、以下に分割統治のプロセスを段階的に示します。

3.1) 16 のグループに分割

       32 ビットは 2 桁のグループで、01 のグループを 16 個設定し、次に 10 のグループを 16 個設定します。

       0x55555555 = 0101 0101 0101 0101 0101 0101 0101 0101

       0xaaaaaaaa = 1010 1010 1010 1010 1010 1010 1010 1010

       そして、数値 A の演算により、各グループの 1 の数値を取得できます。たとえば、11 と 01 は 01、11 と 10 は 10 を取得し、10 を 1 つ右にシフトして 01 になり、01+01 は 10 を取得します。 2、つまり 11 は 1 の数です。

       上記の計算により、数値 A は A1: 1010 1010 1010 1010 1010 1010 1010 1010 となります。

3.2) 8つのグループに分割

       32 ビットは 4 つのグループで、0011 のグループを 8 つ設定し、次に 1100 のグループを 8 つ設定します。

       0x33333333 = 0011 0011 0011 0011 0011 0011 0011 0011

       0xcccccccc = 1100 1100 1100 1100 1100 1100 1100 1100

       また、数値 A1 を使用すると、各グループの数値 1 を取得できます。たとえば、1010 と 0011 は 0010、1010 と 1100 は 1000、1000 は 2 だけ右にシフトされて 0010、0010+0010 は 0100 となり、4 になります。 、これは 1111 1 という数字です。賢い生徒はここでパターンを発見しました。これは、2 組の 2 桁の数字を 1 組の 4 桁の数字に結合したものと考えることができます。

       上記の計算後、数値 A1 は A2: 0100 0100 0100 0100 0100 0100 0100 0100 となります。

3.3) 4 つのグループに分割

       32 ビットは 8 ビットのグループで、0000 1111 のグループを 4 つ設定し、次に 1111 0000 のグループを 4 つ設定します。

       0x0f0f0f0f = 0000 1111 0000 1111 0000 1111 0000 1111

       0xf0f0f0f0 = 1111 0000 1111 0000 1111 0000 1111 0000

       また、数値 A2 の演算により、各グループの 1 の数値を取得できます。たとえば、0100 0100 と 0000 1111 で 0000 0100、0100 0100 と 1111 0000 で 0100 0000 が得られ、0100 0000 右シフト 4 は 0000 0100, 0000 0 になります。 100 +0000 0100 は、1111 1111 の 1 の数である 8 である 0000 1000 を取得します。各数値グループの最終結果は、実際には、数値グループ内の 1 の数です。

       上記の計算により、数値 A2 は A3: 0000 1000 0000 1000 0000 1000 0000 1000 となります。

3.4) 2 つのグループに分割

       32 ビットを 16 ビットのグループとして設定し、0000 0000 1111 1111 を 2 グループ設定し、次に 1111 1111 0000 0000 を 2 グループ設定します。

       0x00ff00ff = 0000 0000 1111 1111 0000 0000 1111 1111

       0xff00ff00 = 1111 1111 0000 0000 1111 1111 0000 0000

       また、数値 A3 を使用した演算により、各グループの 1 の数値を取得できます。たとえば、0000 1000 0000 1000 と 0000 0000 1111 1111 を実行すると、0000 0000 0000 1000,0000 1000 0000 1000 と 1111 1111 0000 00 が得られます。 00 を取得すると 000 0 1000 0000 0000次に、0000 1000 0000 0000 を 8 だけ右にシフトすると、0000 0000 0000 1000、0000 0000 0000 1000+0000 0000 0000 1000 となり、0000 0000 0001 0000、つまり 16 になります。 1111 1111 1111 1111 に 1 の割合。

       上記の計算により、数値 A3 は A4: 0000 0000 0001 0000 0000 0000 0001 0000 となります。

3.5) 1つのグループに分ける

       32ビットを1セットとします。

       0x0000ffff = 0000 0000 0000 0000 1111 1111 1111 1111

       0xffff0000 = 1111 1111 1111 1111 0000 0000 0000 0000

       数値 A4 を使用した AND 演算では、各グループ内の 1 の数を取得できます。つまり、0000 0000 0001 0000 と 0000 0000 0001 0000 の合計は 0000 0000 0010 0000 に等しく、つまり 32 です。

       この数値Aを使用する理由は、この数値がこの手法のロジックにとって直感的に得られるためである。

       上記の計算により、数値 A4 は A5: 0000 0000 0000 0000 0000 0000 0010 0000 となります。それが最終結果です。

テストコード:

1) 循環ビット単位比較

class Solution {
public:
    // 1的个数
    int  NumberOf1(int n) {
        int count = 0;
        //遍历32位
        for(int i = 0; i < 32; ++i){
            //按位比较
            if((n & (1 << i)) != 0)   
                count++;
        }
        return count;
     }
};

2) ビット演算式 n&(n-1)

class Solution {
public:
    // 1的个数
    int NumberOf1(int n) {
        int count = 0;
        // 当n为0时停止比较
        while(n){  
            n &= n - 1;
            count++;
        }
        return count;
     }
};

3) 分割統治

class Solution {
public:
    // 1的个数
    int NumberOf1(int n) {
        int temp = n;
 
        // 0x55555555 = 0101 0101 0101 0101  0101 0101 0101 0101
        // 0xaaaaaaaa = 1010 1010 1010 1010  1010 1010 1010 1010
        temp = (temp & 0x55555555) + ((temp & 0xaaaaaaaa) >> 1);
 
        // 0x33333333 = 0011 0011 0011 0011  0011 0011 0011 0011
        // 0xcccccccc = 1100 1100 1100 1100  1100 1100 1100 1100
        temp = (temp & 0x33333333) + ((temp & 0xcccccccc) >> 2);
 
        // 0x0f0f0f0f = 0000 1111 0000 1111  0000 1111 0000 1111
        // 0xf0f0f0f0 = 1111 0000 1111 0000  1111 0000 1111 0000
        temp = (temp & 0x0f0f0f0f) + ((temp & 0xf0f0f0f0) >> 4);
 
        // 0x00ff00ff = 0000 0000 1111 1111  0000 0000 1111 1111
        // 0xff00ff00 = 1111 1111 0000 0000  1111 1111 0000 0000
        temp = (temp & 0x00ff00ff) + ((temp & 0xff00ff00) >> 8);
 
        // 0x0000ffff = 0000 0000 0000 0000  1111 1111 1111 1111
        // 0xffff0000 = 1111 1111 1111 1111  0000 0000 0000 0000
        temp = (temp & 0x0000ffff) + ((temp & 0xffff0000) >> 16);
        return temp;
     }
};

おすすめ

転載: blog.csdn.net/zhaitianbao/article/details/131892751