Java 计数1';O(n)和O(logn)中的二进制表示形式,其中n是位数

Java 计数1';O(n)和O(logn)中的二进制表示形式,其中n是位数,java,algorithm,binary,time-complexity,Java,Algorithm,Binary,Time Complexity,我在O(n)和O(logn)中有两个二进制表示的任务计数1。由于第一部分很简单,我不知道如何在O(logn)中计算它们,因为它没有排序或其他任何东西。这可能吗? 到目前为止,我的代码是: public class CountOnes { public static void main(String[] args) { System.out.println("Program to count ones"); countOnesInLinearTime("110");

我在O(n)和O(logn)中有两个二进制表示的任务计数1。由于第一部分很简单,我不知道如何在O(logn)中计算它们,因为它没有排序或其他任何东西。这可能吗? 到目前为止,我的代码是:

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);
  }
}
公共类countone{
公共静态void main(字符串[]args)
{
System.out.println(“计数一的程序”);
countOnesInLinearTime(“110”);
对数时间(“110”);
}
private static void CountonesIn对数时间(长的字符串二进制表示){
//待办事项
}
私有静态void countOnesInLinearTime(字符串二进制表示长){
int numberOfOnes=0;
for(int i=0;i

我发现:但有点不同。

假设您的输入字符串是整数,而不是字符串,使用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; 
}

您可以按如下方式计算
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
第四步(从
1
开始执行
i
),我们执行如下操作:

c = ((c >> 2i) & mask) + (c & mask);
因此,对于
i
-第四步,它是一个
2i
零的重复,然后是
2i
一,重复这个过程直到我们达到64位

这是怎么回事?通过将a
2i
位置向右移动,我们将数字的两部分“对齐”。第一部分是放置在
掩码
有零的位置,另一部分是掩码有一的位置。然后我们总结这两个

在第一步中,这意味着,对于每两个位,我们将位向右对齐,求和,此和的结果(介于
0
2
之间的值,两者都包括在内)可以在两个位上表示。所以现在
c
包含一个32个2位数字的序列,每个数字代表两位数字的总和

在下一次迭代中,我们再次执行对齐,现在我们将这些2位数字中的16位与其左边的相邻数字相加(因此其他16个2位数字),这可以得到从
0
4
的值,因此我们可以表示3位的数字,但我们使用空间来表示4位

因此,每次迭代,我们将
2i-1
数字与其他
2i
数字相加,在O(logn)之后,我们最终将两个n/2位数字相加,得到设置位的总数

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


也就是说,计算任意长算法的速度不能超过Ω(n),因为它需要至少读取每个位一次以确定其值,当然,除非您事先知道可以利用的数字结构。

我假设您需要对
int
long
执行此操作,等等,有什么支持转换的吗?你确定你的解决方案吗<代码>字符串
不是任何东西的二进制表示形式输入是否始终是字符串?如果是这样的话,那么你必须看每个角色。根据你对所链接问题的评论,如果输入一个整数,解决方案是logN,那么这个对数在位数中是什么?在“位数”中不是对数。数字
n
需要atmost
O(logn)
位数才能用二进制表示。所以复杂性是
O(logn)
其中
n
是数字请看标题“…其中n是位数”。我明白了。我认为OP高估了约束。我不是这项任务的作者。我接受这个答案是因为:
这个解决方案的美妙之处在于它的循环次数等于给定整数中的设定位数。
因为我们不是魔术师,所以无法预测未来。@Henry:这正是最后一段的内容。一个人可以在O(n)中求两个任意大整数的和,一个人也可以同时进行掩蔽和移位(无需花费更多),但正如所说的,考虑到算法的时间复杂性,这确实不是O(n log)。这是优化之一,但增长率不是
O(logn)
@Kaidul:如前所述,如果位大小是固定的(大小为n),则需要n个这样的步骤,我们在此假设加法、掩蔽和移位是在恒定时间内完成的,但对于任意大的数字,这是不可能的。在小于ω的范围内,根本不可能计算任意大数的位数;(n) @Kaidul:我对logn步骤的意思是,对于32位
int
,它需要少走一步。对于(假设的)128位数字,需要多走一步,等等。对于256位数字,需要多走一步。但是,这些步骤与所有移位/按位操作和加法一样,不是在恒定时间内运行的,如果我们在软件中实现这些步骤,它们与位数成线性关系,或者在某些情况下在ha中实现O(logn)
0101010101010101
0011001100110011
0000111100001111
0000000011111111
...