查找最大连续一位数(Java)

时间:2016-10-20 14:45:34

标签: java algorithm performance math bit

这实际上是我在HackerRank中发现的一个问题。问题是要找到一个数字中连续一位的最大数量。

例如:

  

Border The number 123 (0111 1011 Base 2) should output "4" (0 1111

我想找到最有效,最紧凑的算法。

这是我最好的镜头:

011)

哪个适用于小数字。但是因为它可以递归地称自己为63次,我认为这不是最有效的方法。

我知道迭代显然更有效,因为Java编译器在没有尾递归的情况下不优化递归。我只是喜欢我可以写一行。真正的问题是,是否有一种比计算轮班更有效的方式?

4 个答案:

答案 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);
  }