我有两个任务-在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,但略有不同。
答案 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的部分。然后,我们将两者加起来。
第一步,这意味着我们将每两位对齐到右边,将它们加起来,然后求和(0
和2
之间的值, (包括两端),可以用两位表示。因此,c
现在包含32个2位数字的序列,每个数字代表两位数字的总和。
在下一次迭代中,我们再次执行对齐,现在我们将这些2位数字中的16个与它们的邻居在左边(所以其他16个2位数字)加起来,这可以得出{ {1}}到0
,因此我们可以表示3位的数量,但是我们将空间用于4位。
因此,每个迭代我们将4
个数字与其他2i-1
相加,并且在 O(log n)之后,我们最终将两个 n / 2 < / em>位号,得出设置位的总数。
这里我们假设可以在恒定时间内将两个数字相加,也可以在恒定时间内进行移位和屏蔽。如果数字是任意大数,则不是这种情况,因此严格来说,该算法不是 O(log n),实际上对于任意大数,该算法甚至更糟。
话虽如此,您不能以比Ω(n)更快的速度计算任意长算法,因为它至少需要读取每个位一次才能确定其值,除非您当然知道关于您可以利用的数字的结构。