这实际上是我在HackerRank中发现的一个问题。问题是要找到一个数字中连续一位的最大数量。
例如:
Border
的The number 123 (0111 1011 Base 2) should output "4" (0
1111
我想找到最有效,最紧凑的算法。
这是我最好的镜头:
011)
哪个适用于小数字。但是因为它可以递归地称自己为63次,我认为这不是最有效的方法。
我知道迭代显然更有效,因为Java编译器在没有尾递归的情况下不优化递归。我只是喜欢我可以写一行。真正的问题是,是否有一种比计算轮班更有效的方式?
答案 0 :(得分:2)
您可以将递归转换为tail-recursion以获得增强的性能。它对堆栈的使用更加节省。如果您不了解尾递归的含义,请阅读之前的链接。
public class ConsecutiveOnes
{
public static void main(String[] args)
{
long num = 123;
System.out.println(num + " has " + getMaxBits(num) + " consecutive 1's");
}
public static int GetMaxBits(long number)
{
return accGetMaxBits(number, 0);
}
private static int accGetMaxBits(long number, int accum)
{
if( number == 0 ) return accum;
accum += 1;
return accGetMaxBits(number & (number >>> 1), accum);
}
}
尝试使用-1
(长),0xFFFFFFFF
然后将尾部版本与您的版本进行比较
long num = 0xFFFFFFFF;
System.out.println(num + " has " + accGetMaxBits(num) + " consecutive 1's");
// Out: -1 has 64 consecutive 1's
答案 1 :(得分:2)
Here's an explicit way of doing it i.e. there probably is a more compact/efficient implementation but it may at least be more intuitive to understand.
count = 0
max = 0
while n > 0
if first bit (from the right) is 1
increment count
else
if count > max
max = count
reset count back to 0
set n equal to itself right-shifted over 1
return max
in java:
static int countBits(int n) {
int max = 0;
int count = 0;
while(n > 0){
if((n & 1) == 1) count++;
else {
if (count > max) max = count;
count = 0;
}
if (count > max) max = count;
n = n >> 1;
}
return max;
}
public static void main(String[] args){
int n = 0b1110001101111;
System.out.println(countBits(n));
}
Output:
4
答案 2 :(得分:1)
BitSet bitSet = BitSet.valueOf(new long[] {123});
int count = 0;
int max = 0;
for (int i=0; i < bitSet.size(); i++) {
if(bitSet.get(i)) {
count++;
} else {
max = Math.max(max, count);
count = 0;
}
}
System.out.println(max);
所以我准备了JMH基准:
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 10, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Fork(1)
@State(Scope.Benchmark)
public class MyBenchmark {
@Param({"0", "1", "255", "4294967295", "-1"})
public long value;
@Benchmark
public int testBitSet() {
int count = 0;
int max = 0;
BitSet bitSet = BitSet.valueOf(new long[]{value});
for (int i = 0; i < bitSet.size(); i++) {
if (bitSet.get(i)) {
count++;
} else {
max = Math.max(max, count);
count = 0;
}
}
return max;
}
@Benchmark
public int testBitWiseOperation() {
int max = 0;
int count = 0;
while (value > 0) {
if ((value & 1) == 1) count++;
else {
if (count > max) max = count;
count = 0;
}
if (count > max) max = count;
value = value >> 1;
}
return max;
}
@Benchmark
public int testRecursion() {
return getMaxBits(value);
}
public static int getMaxBits(long number) {
return accGetMaxBits(number, 0);
}
private static int accGetMaxBits(long number, int accum) {
if (number == 0) return accum;
accum += 1;
return accGetMaxBits(number & (number >>> 1), accum);
}
}
这里有结果:
# Run complete. Total time: 00:03:49
Benchmark (value) Mode Cnt Score Error Units
MyBenchmark.testBitSet 0 avgt 5 3,570 ? 0,019 ns/op
MyBenchmark.testBitSet 1 avgt 5 84,515 ? 2,188 ns/op
MyBenchmark.testBitSet 255 avgt 5 85,238 ? 0,581 ns/op
MyBenchmark.testBitSet 4294967295 avgt 5 80,629 ? 0,816 ns/op
MyBenchmark.testBitSet -1 avgt 5 66,905 ? 1,446 ns/op
MyBenchmark.testBitWiseOperation 0 avgt 5 2,200 ? 0,297 ns/op
MyBenchmark.testBitWiseOperation 1 avgt 5 2,164 ? 0,011 ns/op
MyBenchmark.testBitWiseOperation 255 avgt 5 2,166 ? 0,030 ns/op
MyBenchmark.testBitWiseOperation 4294967295 avgt 5 2,172 ? 0,047 ns/op
MyBenchmark.testBitWiseOperation -1 avgt 5 2,164 ? 0,028 ns/op
MyBenchmark.testRecursion 0 avgt 5 2,171 ? 0,015 ns/op
MyBenchmark.testRecursion 1 avgt 5 2,460 ? 0,029 ns/op
MyBenchmark.testRecursion 255 avgt 5 9,546 ? 0,090 ns/op
MyBenchmark.testRecursion 4294967295 avgt 5 31,357 ? 0,389 ns/op
MyBenchmark.testRecursion -1 avgt 5 66,708 ? 0,349 ns/op
从表面上看,我的解决方案输了,但是如果你有一个非常大的位数组,其大小超过了多长?
P.S。 - 欢迎任何对代码的关注。
答案 3 :(得分:0)
正如Shawn Chin在this answer中所描述的,这是Java的一个端口:
public static void count_consecutive_ones(int in) {
int count = 0;
while (in>0) {
in = (in & (in << 1));
count++;
}
System.out.println(count);
}
public static void main(String[] args) {
count_consecutive_ones(15);
}