【问题标题】:Number of 1s in the two's complement binary representations of integers in a range范围内整数的二进制补码二进制表示中 1 的数量
【发布时间】:2011-12-18 01:39:01
【问题描述】:

这个问题来自 2011 Codesprint (http://csfall11.interviewstreet.com/):

计算机科学的基础之一是了解数字在 2 的补码中的表示方式。想象一下,您使用 32 位以 2 的补码表示形式写下 A 和 B 之间的所有数字。你一共要写多少个1? 输入: 第一行包含测试用例的数量 T (

示例输入: 3 -2 0 -3 4 -1 4 样本输出: 63 99 37

说明: 对于第一种情况,-2 包含 31 个 1,后跟一个 0,-1 包含 32 个 1,0 包含 0 个 1。因此总数为 63。 对于第二种情况,答案是 31 + 31 + 32 + 0 + 1 + 1 + 2 + 1 = 99

我意识到您可以利用 -X 中 1 的数量等于 (-X) = X-1 的补码中 0 的数量这一事实来加快搜索速度。该解决方案声称存在用于生成答案的 O(log X) 递归关系,但我不明白。解决方案代码可以看这里:https://gist.github.com/1285119

如果有人能解释这种关系是如何得出的,我将不胜感激!

【问题讨论】:

标签: algorithm binary recurrence


【解决方案1】:

在以下代码中,x 的位和定义为 0 和 x(含)之间数字的二进制补码表示中 1 位的计数,其中 Integer.MIN_VALUE

例如:

bitsum(0) is 0   
bitsum(1) is 1   
bitsum(2) is 1   
bitsum(3) is 4

..等

10987654321098765432109876543210 i % 10 for 0 <= i <= 31
00000000000000000000000000000000 0
00000000000000000000000000000001 1
00000000000000000000000000000010 2
00000000000000000000000000000011 3
00000000000000000000000000000100 4
00000000000000000000000000000101 ...
00000000000000000000000000000110
00000000000000000000000000000111 (2^i)-1
00000000000000000000000000001000  2^i
00000000000000000000000000001001 (2^i)+1 
00000000000000000000000000001010 ...
00000000000000000000000000001011 x, 011 = x & (2^i)-1 = 3
00000000000000000000000000001100
00000000000000000000000000001101
00000000000000000000000000001110
00000000000000000000000000001111
00000000000000000000000000010000
00000000000000000000000000010001
00000000000000000000000000010010 18
...
01111111111111111111111111111111 Integer.MAX_VALUE

bitsum的公式为:

bitsum(x) = bitsum((2^i)-1) + 1 + x - 2^i + bitsum(x & (2^i)-1 )

注意 x - 2^i = x & (2^i)-1

负数的处理方式与正数略有不同。在这种情况下,从总位数中减去零的数量:

Integer.MIN_VALUE <= x < -1
Total number of bits: 32 * -x.

负数 x 中 0 的个数等于 -x - 1 中 1 的个数。

public class TwosComplement {
    //t[i] is the bitsum of (2^i)-1 for i in 0 to 31.
    private static long[] t = new long[32];
    static {
        t[0] = 0;
        t[1] = 1;
        int p = 2;
        for (int i = 2; i < 32; i++) {
            t[i] = 2*t[i-1] + p;
            p = p << 1;
        }
    }

    //count the bits between x and y inclusive
    public static long bitsum(int x, int y) {
        if (y > x && x > 0) {
            return bitsum(y) - bitsum(x-1);
        }
        else if (y >= 0 && x == 0) {
            return bitsum(y);
        }
        else if (y == x) {
            return Integer.bitCount(y);
        }
        else if (x < 0 && y == 0) {
            return bitsum(x);
        } else if (x < 0 && x < y && y < 0 ) {
            return bitsum(x) - bitsum(y+1);
        } else if (x < 0 && x < y && 0 < y) {
            return bitsum(x) + bitsum(y);
        }
        throw new RuntimeException(x + " " + y);
    }

    //count the bits between 0 and x
    public static long bitsum(int x) {
        if (x == 0) return 0;
        if (x < 0) {
            if (x == -1) {
                return 32;
            } else {
                long y = -(long)x;
                return 32 * y - bitsum((int)(y - 1));
            }
        } else {
            int n = x;
            int sum = 0;     //x & (2^i)-1
            int j = 0;
            int i = 1;       //i = 2^j
            int lsb = n & 1; //least significant bit
            n = n >>> 1;
            while (n != 0) {
                sum += lsb * i;
                lsb = n & 1;
                n = n >>> 1;
                i = i << 1;
                j++;
            }
            long tot = t[j] + 1 + sum + bitsum(sum);
            return tot;
        }
    }
}

【讨论】:

    【解决方案2】:

    当 a 为正时,更好的解释已经发布。

    如果 a 为负数,则在 32 位系统上,a 和 0 之间的每个负数将具有 32 个 1 位,减去从 0 到正 a 的二进制表示范围内的位数。

    所以,以更好的方式,

    long long solve(int a) {
        if (a >= 0){
            if (a == 0) return 0;
            else if ((a %2) == 0) return solve(a - 1) + noOfSetBits(a);
            else return (2 * solve( a / 2)) + ((long long)a + 1) / 2;
        }else {
            a++;
            return ((long long)(-a) + 1) * 32 - solve(-a);
        }
    }
    

    【讨论】:

      【解决方案3】:

      将数组转换为一系列整数。然后对每个整数做:

      int NumberOfSetBits(int i)
      {
         i = i - ((i >> 1) & 0x55555555);
         i = (i & 0x33333333) + ((i >> 2) & 0x33333333);
         return (((i + (i >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24;
      }
      

      这也是可移植的,不像 __builtin_popcount

      请看这里:How to count the number of set bits in a 32-bit integer?

      【讨论】:

        【解决方案4】:

        嗯,它并不那么复杂......

        单参数solve(int a) 函数是关键。它很短,所以我将它剪切并粘贴在这里:

        long long solve(int a)
        {
         if(a == 0) return 0 ;
         if(a % 2 == 0) return solve(a - 1) + __builtin_popcount(a) ;
         return ((long long)a + 1) / 2 + 2 * solve(a / 2) ;
        }
        

        只对非负a有效,计算0到a(含)所有整数中1的位数。

        函数分三种情况:

        a == 0 -> 返回 0。显然。

        a even -> 返回a 加上solve(a-1) 中1 的位数。也很明显。

        最后一个案例很有趣。那么,我们如何计算从0到奇数a的1位数呢?

        考虑 0 到 a 之间的所有整数,并将它们分成两组:偶数和赔率。例如,如果a 为 5,则您有两个组(二进制):

        000  (aka. 0)
        010  (aka. 2)
        100  (aka. 4)
        

        和

        001  (aka 1)
        011  (aka 3)
        101  (aka 5)
        

        注意这两个组必须具有相同的大小(因为a 是奇数并且范围是包含在内)。要计算每组中有多少个 1,首先数除最后一位之外的所有位,然后数最后一位。

        除了最后几位之外的所有内容如下所示:

        00
        01
        10
        

        ...both 组看起来像这样。这里的 1 位数就是solve(a/2)。 (在本例中,它是从 0 到 2 的 1 位数。另外,请回忆一下 C/C++ 中的整数除法 向下。)

        对于第一组中的每个数字,最后一位为零,对于第二组中的每个数字,最后一位为零,因此最后一位为总数贡献(a+1)/2 一位。

        所以递归的第三种情况是(a+1)/2 + 2*solve(a/2),对long long 进行适当的强制转换以处理a 是INT_MAX 的情况(因此a+1 溢出)。

        这是一个 O(log N) 的解决方案。要将其推广到solve(a,b),您只需计算solve(b) - solve(a),加上担心负数的适当逻辑。这就是两个参数solve(int a, int b) 正在做的事情。

        【讨论】:

        • 感谢@Nemo,您的解释很有道理!
        • 这是做什么的 __builtin_popcount(a) ??
        • @nikhil - 这是一个GCC built-in,用于计算整数中 1 的位数。
        • 你想要solve(b) - solve(a-1)而不是最后一段中的solve(b) - solve(a)吗?
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-06-08
        • 2021-04-20
        • 2021-12-21
        • 2012-04-17
        相关资源
        最近更新 更多