我知道我可以编写以下方法来计算long的设置位索引:
private static List<Integer> bitPositions(long number) {
final List<Integer> positions = new ArrayList<>();
int position = 1;
while (number != 0) {
if ((number & 1L) != 0) {
positions.add(position);
}
position++;
number = number >>> 1;
}
return positions;
}
我的问题是:有更快的方法吗?
答案 0 :(得分:4)
BitBank's answer to this question的速度大约是这个答案的两个方法的两倍。这在BitBank的答案中窃取了这个想法并使其比我在我的机器上快了73%(比问题的方法快9倍),通过使用bit twiddling来重复关闭最不重要的一位而不是右移一位离开右端并跟踪发生了多少变化。
private static final byte[] bitPositions(long n) {
final byte[] result = new byte[Long.bitCount(n)];
for (int i = 0; n != 0L; i++) {
result[i] = (byte) ((byte) Long.numberOfTrailingZeros(n) + 1);
n &= n - 1L; // Change least-significant one bit to a zero bit.
}
return result;
}
byte
的双重加速可以加快速度。我认为这是因为它允许byte
大小而不是int
大小的算术。正如Durandal在问题评论中指出的那样,您可以交换以下内容:
for (int bitPosition : bitPositions(n)) {
// Do something with `bitPosition`.
}
对于跳过方法调用的样式而不是这样做:
long temp = n;
while (temp != 0L) {
int bitPosition = Long.numberOfTrailingZeros(temp) + 1;
temp &= temp - 1L; // Change least-significant one bit to a zero bit.
// Do something with `bitPosition`.
}
如果要通过相同数字的位位置多次迭代迭代,则必须重新计算位位置,而不是重复使用先前生成的数组。
但这可能不是一个实际的缺点,如果重新计算位位置比从RAM中的数组加载预先计算的位置更快。
这是一种产生相同结果的方法(仅在byte[]
而不是List<Integer>
)大约快两倍:
private static final byte[] bitPositions(long n) {
final byte[] result = new byte[Long.bitCount(n)];
int i = 0;
for (byte bit = 1; n != 0L; bit++) {
if ((n & 1L) != 0) result[i++] = bit;
n >>>= 1;
}
return result;
}
我建议将byte bit = 1
循环中的for
更改为byte bit = 0
,以切换到从0开始而不是以1开头编号位位置的传统方法。
Long.bitCount(n)
预先计算所需的容量(使用处理器中非常快速的“popcount”指令)可以大大加快您的方法速度。您可以使用ArrayList
。new ArrayList<>(Long.bitCount(n))
来更改此设置
ArrayList<Integer>
比byte[]
慢,因为:
Integer
cache查找低值(-127
到128
)Integer
值,以便将它们放入ArrayList
。int
生成的List<Integer>
时必须浪费时间,因为您必须同时从Integer
检索List<Integer>
然后检索来自int
。{/ li>的Integer
byte[]
使用大约1/4(32位系统)或1/8(64位系统)ArrayList<Integer>
的内存,因为byte
是那么多小于指向Integer
s。正如另一个人删除的答案所暗示的那样,循环展开会加快我的机器上的速度(请检查您的机器上是否也是如此):
private static final byte[] bitPositions(final long n) {
final byte[] result = new byte[Long.bitCount(n)];
int i = 0;
if ((n & 1L) != 0L) result[i++] = 1;
if ((n & 2L) != 0L) result[i++] = 2;
if ((n & 4L) != 0L) result[i++] = 3;
if ((n & 8L) != 0L) result[i++] = 4;
if ((n & 16L) != 0L) result[i++] = 5;
if ((n & 32L) != 0L) result[i++] = 6;
if ((n & 64L) != 0L) result[i++] = 7;
if ((n & 128L) != 0L) result[i++] = 8;
if ((n & 256L) != 0L) result[i++] = 9;
if ((n & 512L) != 0L) result[i++] = 10;
if ((n & 1024L) != 0L) result[i++] = 11;
if ((n & 2048L) != 0L) result[i++] = 12;
if ((n & 4096L) != 0L) result[i++] = 13;
if ((n & 8192L) != 0L) result[i++] = 14;
if ((n & 16384L) != 0L) result[i++] = 15;
if ((n & 32768L) != 0L) result[i++] = 16;
if ((n & 65536L) != 0L) result[i++] = 17;
if ((n & 131072L) != 0L) result[i++] = 18;
if ((n & 262144L) != 0L) result[i++] = 19;
if ((n & 524288L) != 0L) result[i++] = 20;
if ((n & 1048576L) != 0L) result[i++] = 21;
if ((n & 2097152L) != 0L) result[i++] = 22;
if ((n & 4194304L) != 0L) result[i++] = 23;
if ((n & 8388608L) != 0L) result[i++] = 24;
if ((n & 16777216L) != 0L) result[i++] = 25;
if ((n & 33554432L) != 0L) result[i++] = 26;
if ((n & 67108864L) != 0L) result[i++] = 27;
if ((n & 134217728L) != 0L) result[i++] = 28;
if ((n & 268435456L) != 0L) result[i++] = 29;
if ((n & 536870912L) != 0L) result[i++] = 30;
if ((n & 1073741824L) != 0L) result[i++] = 31;
if ((n & 2147483648L) != 0L) result[i++] = 32;
if ((n & 4294967296L) != 0L) result[i++] = 33;
if ((n & 8589934592L) != 0L) result[i++] = 34;
if ((n & 17179869184L) != 0L) result[i++] = 35;
if ((n & 34359738368L) != 0L) result[i++] = 36;
if ((n & 68719476736L) != 0L) result[i++] = 37;
if ((n & 137438953472L) != 0L) result[i++] = 38;
if ((n & 274877906944L) != 0L) result[i++] = 39;
if ((n & 549755813888L) != 0L) result[i++] = 40;
if ((n & 1099511627776L) != 0L) result[i++] = 41;
if ((n & 2199023255552L) != 0L) result[i++] = 42;
if ((n & 4398046511104L) != 0L) result[i++] = 43;
if ((n & 8796093022208L) != 0L) result[i++] = 44;
if ((n & 17592186044416L) != 0L) result[i++] = 45;
if ((n & 35184372088832L) != 0L) result[i++] = 46;
if ((n & 70368744177664L) != 0L) result[i++] = 47;
if ((n & 140737488355328L) != 0L) result[i++] = 48;
if ((n & 281474976710656L) != 0L) result[i++] = 49;
if ((n & 562949953421312L) != 0L) result[i++] = 50;
if ((n & 1125899906842624L) != 0L) result[i++] = 51;
if ((n & 2251799813685248L) != 0L) result[i++] = 52;
if ((n & 4503599627370496L) != 0L) result[i++] = 53;
if ((n & 9007199254740992L) != 0L) result[i++] = 54;
if ((n & 18014398509481984L) != 0L) result[i++] = 55;
if ((n & 36028797018963968L) != 0L) result[i++] = 56;
if ((n & 72057594037927936L) != 0L) result[i++] = 57;
if ((n & 144115188075855872L) != 0L) result[i++] = 58;
if ((n & 288230376151711744L) != 0L) result[i++] = 59;
if ((n & 576460752303423488L) != 0L) result[i++] = 60;
if ((n & 1152921504606846976L) != 0L) result[i++] = 61;
if ((n & 2305843009213693952L) != 0L) result[i++] = 62;
if ((n & 4611686018427387904L) != 0L) result[i++] = 63;
if ((n & -9223372036854775808L) != 0L) result[i++] = 64;
return result;
}
您也可以更改此项以开始计数位位置为零而不是一位。
>>>
。++
。答案 1 :(得分:3)
如果您不介意使用内在函数,则可以使用更快的版本。 Long.numberOfTrailingZeros()
将使用counts the number of consecutive zero bits starting from the least-significant bit(x86处理器上的BSF
instruction)的内在CPU。
对于稀疏值,这将比所有其他循环方法更快,因为它在主循环中没有任何条件或分支,它通过单次迭代跳过任意数量的0的运行,并且对于64位long
,BSF
内在函数在英特尔Haswell CPU上有a latency of only 3 clock cycles。
private static final byte[] bitPositions(long n) {
final byte[] result = new byte[Long.bitCount(n)];
byte bitPosition = 0;
for (int i = 0; n != 0L; i++) {
final byte bitsToSkip = (byte) (Long.numberOfTrailingZeros(n) + 1);
n >>>= bitsToSkip;
bitPosition += bitsToSkip;
result[i] = bitPosition;
}
return result;
}
答案 2 :(得分:1)
这里我使用了一种简单的方法,即次线性时间。您决定是从左侧算起还是从右侧算起,在printf("%ld\n", 32-pos)
中,您可以考虑32-pos
或pos
。
bit_pos(unsigned long x)
{
unsigned long pos;
unsigned long w;
while(x) {
/* extract the rightmost `1` in `w` then remove it */
w = x&-x;
x-=w;
if (w) {
/* compute number of trailing zeros (location of 1) for w */
pos = 1;
if (!(w >> 16)) {pos+=16;w<<=16;}
if (!(w >> 24)) {pos+= 8;w<<= 8;}
if (!(w >> 28)) {pos+= 4;w<<= 4;}
if (!(w >> 30)) {pos+= 2;w<<= 2;}
pos = pos - (w >> 31);
printf("%ld\n", 32-pos);
}
}
printf("\n");
}
main()
{
bit_pos(2UL+4+32+1024);
bit_pos(3456789UL);
}