Largest Power of 2 That Is Less Than the Given Number with Java – 用Java计算小于给定数的最大2次方

最后修改: 2020年 8月 28日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this article, we’ll be seeing how to find the largest power of 2 that is less than the given number.

在这篇文章中,我们将看到如何找到小于给定数的2的最大幂。

For our examples, we’ll take the sample input 9. 20 is 1, the least valid input for which we can find the power of 2 less than the given input is 2. Hence we’ll only consider inputs greater than 1 as valid.

在我们的例子中,我们将采取样本输入9。20是1,我们可以找到小于给定输入的2的幂的最小有效输入是2,因此我们将只考虑大于1的输入为有效。

2. Naive Approach

2.天真的方法

Let’s start with 20, which is 1, and we’ll keep multiplying the number by 2 until we find a number that is less than the input:

让我们从20开始,也就是1,我们将继续乘以2,直到我们找到一个小于输入的数字

public long findLargestPowerOf2LessThanTheGivenNumber(long input) {
    Assert.isTrue(input > 1, "Invalid input");
    long firstPowerOf2 = 1;
    long nextPowerOf2 = 2;
    while (nextPowerOf2 < input) {
        firstPowerOf2 = nextPowerOf2;
        nextPowerOf2 = nextPowerOf2 * 2;
    }
    return firstPowerOf2;
}

Let’s understand the code for sample input = 9.

让我们了解一下样本input=9的代码。

The initial value for firstPowerOf2 is 1 and nextPowerOf2 is 2. As we can see, 2 < 9 is true, and we get inside the while loop.

firstPowerOf2的初始值是1,nextPowerOf2是2。我们可以看到,2<9是真的,我们进入了while循环。

For the first iteration, firstPowerOf2 is 2 and nextPowerOf2 is 2 * 2 = 4. Again 4 < 9 so lets continue the while loop.

对于第一次迭代,firstPowerOf2是2,nextPowerOf2是2*2=4。同样,4<9,所以让我们继续进行while循环。

For the second iteration, firstPowerOf2 is 4 and nextPowerOf2 is 4 * 2 = 8. Now 8 < 9, let’s keep going.

对于第二次迭代,firstPowerOf2是4,nextPowerOf2是4*2=8。 现在8<9,让我们继续。

For the third iteration, firstPowerOf2 is 8 and nextPowerOf2 is 8 * 2 = 16. The while condition 16 < 9 is false, so it breaks out of the while loop. 8 is the largest power of 2 which is less than 9.

对于第三次迭代,firstPowerOf2是8,nextPowerOf2是8*2=16。16<9的while条件是假的,所以它脱离了while循环。8是2的最大幂,小于9。

Let’s run some tests to validate our code:

让我们运行一些测试来验证我们的代码。

assertEquals(8, findPowerOf2LessThanTheGivenNumber(9));
assertEquals(16, findPowerOf2LessThanTheGivenNumber(32));

The time complexity of our solution is O(log2(N)). In our case, we iterated log2(9) = 3 times.

我们解决方案的时间复杂性是O(log2(N))。在我们的案例中,我们迭代了log2(9) = 3次。

3. Using Math.log

3.使用Math.log

Log base 2 will give how many times we can divide a number by 2 recursively in other words, log2 of a number gives the power of 2. Let’s look at some examples to understand this.

对数基数2将给出一个数字可以递归除以2的次数,换句话说,一个数字的对数2给出2的幂。让我们看一些例子来理解这一点。

log2(8) = 3 and log2(16) = 4. In general, we can see that y = log2x where x = 2y.

log2(8)=3,log2(16)=4。一般来说,我们可以看到,y=log2x,其中x=2y

Hence, if we find a number that is divisible by 2, we subtract 1 from it so that we avoid a scenario where the number is a perfect power of 2.

因此,如果我们找到一个可以被2整除的数字,我们就从它身上减去1,这样就可以避免出现这个数字是2的完全幂的情况。

Math.log is log10. To compute log2(x), we can use the formula log2(x)=log10(x)/log10(2)

Math.log是log10。为了计算log2(x),我们可以使用公式log2(x)=log10(x)/log10(2)

Let’s put that in code:

让我们把这句话写成代码。

public long findLargestPowerOf2LessThanTheGivenNumberUsingLogBase2(long input) {
    Assert.isTrue(input > 1, "Invalid input");
    long temp = input;
    if (input % 2 == 0) {
        temp = input - 1;
    }
    long power = (long) (Math.log(temp) / Math.log(2));
    long result = (long) Math.pow(2, power);
    return result;
}

Assuming our sample input as 9, the initial value of temp is 9.

假设我们的样本输入为9,temp的初始值为9。

9 % 2 is 1, so our temp variable is 9. Here we are using modulo division, which will give the remainder of 9/2.

9 % 2是1,所以我们的temp变量是9。这里我们使用模除法,这将得到9/2的余数。

To find the log2(9), we do log10(9) / log10(2) = 0.95424 / 0.30103 ~= 3.

为了找到对数2(9),我们做对数10(9)/对数10(2) = 0.95424 / 0.30103 ~= 3。

Now, the result is 23 which is 8.

现在,结果是23,是8。

Let’s verify our code:

让我们验证一下我们的代码。

assertEquals(8, findLargestPowerOf2LessThanTheGivenNumberUsingLogBase2(9));
assertEquals(16, findLargestPowerOf2LessThanTheGivenNumberUsingLogBase2(32));

In reality, Math.pow will be doing the same iteration that we did in approach 1. Hence we can say that for this solution too, the time complexity is O(Log2(N)).

实际上,Math.pow将进行与我们在方法1中相同的迭代。因此我们可以说,对于这个解决方案,时间复杂度是O(Log2(N))

4. Bitwise Technique

4.比特技术

For this approach, we’ll use the bitwise shift technique. First, let’s look at the binary representations for the power of 2 considering we have 4 bits to represent the number

对于这种方法,我们将使用位移技术。首先,让我们看一下2的幂的二进制表示法,考虑到我们有4个比特来表示这个数字

20 1 0001
21 2 0010
22 4 0100
23 8 1000

Looking closely, we can observe that we can compute the power of 2 by left shifting the bytes for 1. ie. 22 is left shift bytes for 1 by 2 places and so on.

仔细观察,我们可以发现,我们可以通过左移1的字节来计算2的幂。即22是左移1的字节2位,以此类推。

Let’s code using the bitshift technique:

让我们用比特移位技术来编码。

public long findLargestPowerOf2LessThanTheGivenNumberUsingBitShiftApproach(long input) {
    Assert.isTrue(input > 1, "Invalid input");
    long result = 1;
    long powerOf2;
    for (long i = 0; i < Long.BYTES * 8; i++) {
        powerOf2 = 1 << i;
        if (powerOf2 >= input) {
            break;
        }
        result = powerOf2;
    }
    return result;
}

In the above code, we are using long as our data type, which uses 8 bytes or 64 bits. So we’ll be computing the power of 2 up to 264. We are using the bit shift operator << to find the power of 2. For our sample input 9, after the 4th iteration, the value of powerOf2 = 16 and result = 8 where we break out of the loop as 16 > 9 the input.

在上面的代码中,我们使用long作为我们的数据类型,它使用8字节或64位。所以我们将计算2的幂,最高为264。我们使用移位操作符<<来寻找2的幂。对于我们的样本输入9,在第4迭代后,powerOf2的值=16,result=8,我们走出了循环,因为16>9的输入

Let’s check if our code is working as expected:

让我们检查一下我们的代码是否按预期工作。

assertEquals(8, findLargestPowerOf2LessThanTheGivenNumberUsingBitShiftApproach(9));
assertEquals(16, findLargestPowerOf2LessThanTheGivenNumberUsingBitShiftApproach(32));

The worst-case time complexity for this approach is again O(log2(N)), similar to what we saw for the first approach. However, this approach is better as a bit shift operation is more efficient compared to multiplication.

这种方法的最坏情况下的时间复杂度也是O(log2(N)),与我们在第一种方法中看到的情况类似。然而,这种方法更好,因为与乘法相比,位移操作更有效率

5. Bitwise AND

5.顺位AND

For our next approach, we’ll be using this formula 2n AND 2n -1 = 0.

对于我们接下来的方法,我们将使用这个公式2n AND 2n -1 = 0

Let’s look at some examples to understand how it works.

让我们看一些例子来了解它是如何工作的。

The binary representation of 4 is 0100, and 3 is 0011.

4的二进制表示是0100,,3是0011

Let’s do a bitwise AND operation on these two numbers. 0100 AND 0011 is 0000. We can say the same for any power of 2 and a number less than it. Let’s take 16 (24) and 15 which is represented as 1000, 0111 respectively. Again, we see that the bitwise AND on these two results in 0. We can also say that the AND operation on any other number apart from these 2 won’t result in a 0.

让我们对这两个数字做一个bitwise AND操作01000011就是0000。我们可以对任何2的幂和小于它的数字说同样的话。让我们拿16(24)和15分别表示为10000111。我们还可以说,除了这两个数字之外,对任何其他数字进行AND操作都不会得到0。

Let’s see the code for solving this problem using bitwise AND:

让我们看看使用位数和的方式解决这个问题的代码。

public long findLargestPowerOf2LessThanTheGivenNumberUsingBitwiseAnd(long input) { 
    Assert.isTrue(input > 1, "Invalid input");
    long result = 1;
    for (long i = input - 1; i > 1; i--) {
        if ((i & (i - 1)) == 0) {
            result = i;
            break;
        }
    }
    return result;
}

In the above code, we loop over numbers less than our input. Whenever we find the bitwise AND of a number and number-1 is zero, we break out of the loop, as we know that number will be a power of 2. In this case for our sample input 9, we break out of the loop when i = 8 and i – 1 = 7.

在上面的代码中,我们在小于我们输入的数字上循环。每当我们发现一个数字的位数和数字-1为0时,我们就退出循环,因为我们知道这个数字将是2的幂。在这种情况下,对于我们的样本输入9,我们在i=8和i-1=7时退出循环。

Now, let’s verify a couple of scenarios:

现在,让我们来验证一下几种情况。

assertEquals(8, findLargestPowerOf2LessThanTheGivenNumberUsingBitwiseAnd(9));
assertEquals(16, findLargestPowerOf2LessThanTheGivenNumberUsingBitwiseAnd(32));

The worst-case time complexity for this approach is O(N/2) when the input is an exact power 2. As we can see, this is not the most efficient solution, but it is good to know this technique as it could come handy when tackling similar problems.

当输入为精确的2次方时,这种方法的最坏情况时间复杂度为O(N/2)。正如我们所看到的,这不是最有效的解决方案,但了解这种技术是很好的,因为它在处理类似问题时可能很方便。

6. Conclusion

6.结语

We have seen different approaches for finding the largest power of 2 that is less than the given number. We also noticed how bitwise operations can simplify computations in some cases.

我们已经看到了不同的方法来寻找小于给定数字的2的最大幂。我们还注意到,在某些情况下,位操作可以简化计算。

The complete source code with unit tests for this article can be found over on GitHub.

这篇文章的完整源代码和单元测试可以在GitHub上找到