Найбільша потужність 2, що менше заданого числа в Java

1. Огляд

У цій статті ми побачимо, як знайти найбільшу потужність 2, меншу за задане число.

Для наших прикладів ми візьмемо зразок вводу 9. 20 - 1, найменш дійсний вхід, для якого ми можемо знайти потужність на 2 менше, ніж заданий вхід - 2. Отже, ми вважатимемо дійсними лише входи більше 1 .

2. Наївний підхід

Почнемо з 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; }

Давайте зрозуміємо код для введення зразка = 9.

Початкове значення для firstPowerOf2 дорівнює 1, а nextPowerOf2 дорівнює 2. Як бачимо, 2 <9 є істинним, і ми потрапляємо всередину циклу while.

Для першої ітерації firstPowerOf2 дорівнює 2, а nextPowerOf2 дорівнює 2 * 2 = 4. Знову 4 <9, щоб продовжувати цикл while.

Для другої ітерації firstPowerOf2 дорівнює 4, а nextPowerOf2 дорівнює 4 * 2 = 8. Тепер 8 <9, продовжуймо.

Для третьої ітерації firstPowerOf2 дорівнює 8, а nextPowerOf2 дорівнює 8 * 2 = 16. Умова while 16 <9 хибна, тому вона виривається з циклу while. 8 - найбільша потужність 2, яка менше 9.

Давайте проведемо кілька тестів для перевірки нашого коду:

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

Складність часу нашого рішення становить O (log 2 (N)) . У нашому випадку ми повторювали журнал 2 (9) = 3 рази.

3. Використання Math.log

Журнал бази 2 покаже, скільки разів ми можемо ділити число на 2 рекурсивно, іншими словами, журнал 2 числа дає ступінь 2 . Давайте розглянемо кілька прикладів, щоб зрозуміти це.

log 2 (8) = 3 і log 2 (16) = 4. Загалом ми можемо бачити, що y = log 2 x, де x = 2y.

Отже, якщо ми знаходимо число, яке ділиться на 2, ми віднімаємо з нього 1, щоб уникнути сценарію, коли число є ідеальною степенем 2.

Math.log - журнал 10 . Для обчислення журналу 2 (x) ми можемо використовувати формулу log 2 (x) = log 10 (x) / log 10 (2)

Давайте помістимо це в код:

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; }

Приймаючи наш вибірковий вхід як 9, початкове значення temp дорівнює 9.

9% 2 - це 1, тому наша змінна temp дорівнює 9.Тут ми використовуємо модульний поділ, який дасть залишок 9/2.

Щоб знайти журнал 2 (9), ми робимо журнал 10 (9) / журнал 10 (2) = 0,95424 / 0,30103 ~ = 3.

Тепер результат 23, це 8.

Давайте перевіримо наш код:

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

Насправді Math.pow буде виконувати ту саму ітерацію, що і в підході 1. Отже, ми можемо сказати, що і для цього рішення часова складність становить O (Log 2 (N)) .

4. Побітова техніка

Для цього підходу ми будемо використовувати техніку побітового зсуву. По-перше, давайте розглянемо двійкові подання степеня 2, враховуючи, що у нас є 4 біти для представлення числа

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

Придивившись, ми можемо помітити, що ми можемо обчислити потужність 2, зсуваючи ліві байти на 1 . тобто 22 - це ліві байти зсуву на 1 на 2 місця тощо.

Давайте кодуватимемо, використовуючи техніку бітового зсуву:

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 <= input) { break; } result = powerOf2; } return result; }

У наведеному вище коді, ми використовуємо довгі , як наш тип даних, який використовує 8 байт або 64 біт. Отже, ми будемо обчислювати потужність від 2 до 264. Ми використовуємо оператор бітового зсуву <<, щоб знайти потужність 2. Для нашого вибіркового входу 9, після 4-ї ітерації, значення powerOf2 = 16 і result = 8, де ми вириваємося з циклу, коли 16> 9 вхід .

Давайте перевіримо, чи працює наш код належним чином:

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

Гіршому випадку складність такого підходу знову O (вхід 2 (N)) , подібно до того , що ми бачили в першому наближенні. Однак такий підхід кращий, оскільки операція зсуву бітів є більш ефективною порівняно з множенням .

5. Побітове І

For our next approach, we'll be using this formula 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.

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.

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.

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.

6. Conclusion

Ми бачили різні підходи до пошуку найбільшої потужності 2, яка менша від заданого числа. Ми також помітили, як побітові операції можуть спростити обчислення в деяких випадках.

Повний вихідний код із модульними тестами для цієї статті можна знайти на GitHub.