以O(n)和O(log n)的二进制表示形式计数1,其中n是位数

时间:2019-05-12 11:53:41

标签: java algorithm binary time-complexity

我有两个任务-在O(n)和O(log n)中以二进制表示形式计数1。由于第一部分很简单,我无法弄清楚如何将它们归类为O(log n),因为它没有排序或其他任何内容。那有可能吗? 到目前为止,我的代码:

public class CountOnes {
  public static void main(String[] args)
  {
    System.out.println("Program to count ones");
    countOnesInLinearTime("110");
    countOnesInlogarithmicTime("110");
  }

  private static void countOnesInlogarithmicTime(String binaryRepresentationOfLong) {
    //TODO
  }

  private static void countOnesInLinearTime(String binaryRepresentationOfLong) {
    int numberOfOnes = 0;
    for(int i = 0; i < binaryRepresentationOfLong.length(); i++)
    {
      if(binaryRepresentationOfLong.charAt(i) == '1')
      {
        numberOfOnes++;
      }
    }
    System.out.println(numberOfOnes);
  }
}

我发现:Count number of 1's in binary representation,但略有不同。

2 个答案:

答案 0 :(得分:4)

假设您输入的字符串将是整数,而不是字符串,则可以使用Brian Kernighan的算法来实现:

从数字中减去1将切换所有位(从右到左),直到最右边的设置位(包括最右边的设置位)。因此,如果我们将数字减1并对其本身按位与(n & (n-1)),则会取消设置最右边的设置位。如果我们在循环中执行n(n-1)并计算循环执行的次数,我们将获得设置的位数。

此解决方案的优点在于,它循环的次数等于给定整数中的设置位数。

1. Initialize count: = 0
2. If integer n is not zero
      (a) Do bitwise & with (n-1) and assign the value back to n
          n: = n&(n-1)
      (b) Increment count by 1
      (c) go to step 2
3. Else return count

实施

int countNumberOfOnes(int n) { 
    int count = 0; 
    while (n > 0) { 
        n &= (n - 1); 
        count++; 
    } 
    return count; 
}

答案 1 :(得分:2)

您可以按以下方式计算long中的设置位数:

long l = 1425142514251425142L; // some value
long c = l;
c = ((c >> 1) & 0x5555555555555555L) + (c & 0x5555555555555555L);
c = ((c >> 2) & 0x3333333333333333L) + (c & 0x3333333333333333L);
c = ((c >> 4) & 0x0f0f0f0f0f0f0f0fL) + (c & 0x0f0f0f0f0f0f0f0fL);
c = ((c >> 8) & 0x00ff00ff00ff00ffL) + (c & 0x00ff00ff00ff00ffL);
c = ((c >> 16) & 0x0000ffff0000ffffL) + (c & 0x0000ffff0000ffffL);
c = ((c >> 32) & 0x00000000ffffffffL) + (c & 0x00000000ffffffffL);

我们基本上执行 O(n)次,用 n 位数进行类似的操作。对于第i步(从i开始1),我们执行以下操作:

c = ((c >> 2i) & mask) + (c & mask);

掩码具有二进制结构:

0101010101010101
0011001100110011
0000111100001111
0000000011111111
...

因此,对于第i步,它是2i零的重排,之后是2i个零,并重复进行直到我们达到64位。

这是如何工作的?通过向右移动2i位置,我们将数字的两部分“对齐”。第一部分是mask处为零的部分,另一部分是掩码为1的部分。然后,我们将两者加起来。

第一步,这意味着我们将每两位对齐到右边,将它们加起来,然后求和(02之间的值, (包括两端),可以用两位表示。因此,c现在包含32个2位数字的序列,每个数字代表两位数字的总和。

在下一次迭代中,我们再次执行对齐,现在我们将这些2位数字中的16个与它们的邻居在左边(所以其他16个2位数字)加起来,这可以得出{ {1}}到0,因此我们可以表示3位的数量,但是我们将空间用于4位。

因此,每个迭代我们将4个数字与其他2i-1相加,并且在 O(log n)之后,我们最终将两个 n / 2 < / em>位号,得出设置位的总数。

这里我们假设可以在恒定时间内将两个数字相加,也可以在恒定时间内进行移位和屏蔽。如果数字是任意大数,则不是这种情况,因此严格来说,该算法不是 O(log n),实际上对于任意大数,该算法甚至更糟。

话虽如此,您不能以比Ω(n)更快的速度计算任意长算法,因为它至少需要读取每个位一次才能确定其值,除非您当然知道关于您可以利用的数字的结构。