在原始InterviewStreet Codesprint上,有一个关于计算a和b之间的数字的二进制补码表示中的数量的问题。我能够通过迭代传递所有测试用例的准确性,但我只能在正确的时间内传递两个。有提示提到找到一个递归关系,所以我切换到递归,但最终花了相同的时间。那么有人能找到比我提供的代码更快的方法吗?输入文件的第一个数字是文件中的测试用例。我在代码之后提供了一个示例输入文件。
import java.util.Scanner;
public class Solution {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int numCases = scanner.nextInt();
for (int i = 0; i < numCases; i++) {
int a = scanner.nextInt();
int b = scanner.nextInt();
System.out.println(count(a, b));
}
}
/**
* Returns the number of ones between a and b inclusive
*/
public static int count(int a, int b) {
int count = 0;
for (int i = a; i <= b; i++) {
if (i < 0)
count += (32 - countOnes((-i) - 1, 0));
else
count += countOnes(i, 0);
}
return count;
}
/**
* Returns the number of ones in a
*/
public static int countOnes(int a, int count) {
if (a == 0)
return count;
if (a % 2 == 0)
return countOnes(a / 2, count);
else
return countOnes((a - 1) / 2, count + 1);
}
}
输入:
3
-2 0
-3 4
-1 4
Output:
63
99
37
答案 0 :(得分:2)
第一步是替换
public static int countOnes(int a, int count) {
if (a == 0)
return count;
if (a % 2 == 0)
return countOnes(a / 2, count);
else
return countOnes((a - 1) / 2, count + 1);
}
重复到log 2 a的深度,具有更快的实现,例如着名的bit-twiddling
public static int popCount(int n) {
// count the set bits in each bit-pair
// 11 -> 10, 10 -> 01, 0* -> 0*
n -= (n >>> 1) & 0x55555555;
// count bits in each nibble
n = ((n >>> 2) & 0x33333333) + (n & 0x33333333);
// count bits in each byte
n = ((n >> 4) & 0x0F0F0F0F) + (n & 0x0F0F0F0F);
// accumulate the counts in the highest byte and shift
return (0x01010101 * n) >> 24;
// Java guarantees wrap-around, so we can use int here,
// in C, one would need to use unsigned or a 64-bit type
// to avoid undefined behaviour
}
使用四个移位,五个按位和一个减法,两个加法和一个乘法,总共十三个非常便宜的指令。
但除非范围非常小,否则可以比计算每个数字的位更好更多。
让我们先考虑非负数。从0到2 k -1的数字都设置为k
位。每个位都设置为其中的一半,因此总位数为k*2^(k-1)
。现在让2^k <= a < 2^(k+1)
。数字0 <= n <= a
中的总位数是数字0 <= n < 2^k
中的位数和数字2^k <= n <= a
中的位数之和。如上所述,第一个计数是k*2^(k-1)
。在第二部分中,我们有a - 2^k + 1
个数字,每个数字都有2个 k 位设置,忽略前导位,这些数字的位数与数字中的相同{ {1}},所以
0 <= n <= (a - 2^k)
现在为负数。在二分之一补码totalBits(a) = k*2^(k-1) + (a - 2^k + 1) + totalBits(a - 2^k)
中,数字-(n+1) = ~n
是数字-a <= n <= -1
的补码,数字0 <= m <= (a-1)
中的总设置位数为-a <= n <= -1
对于a*32 - totalBits(a-1)
范围内的总位数,我们必须加或减,这取决于范围的两端是否具有相反或相同的符号。
a <= n <= b