Как работает побитовая операция на логических значениях?

26

Я столкнулся с этой проблемой в Edabit и не смог разработать это побитовое решение.

notNotNot = (a,b) => !!(a%2 >> b)

Соревнование:

//Something which is not true is false, but something which is not not true is true! 
//Create a function where given n number of "not", evaluate whether it's true or false.

//Examples:
notNotNot(1, true)  false
// Not true

notNotNot(2, false)  false
// Not not false

notNotNot(6, true)  true
// Not not not not not not true

Я провел некоторое исследование, что этот оператор:

Сдвиг вправо, толкая копии самого левого бита слева, и пусть самые правые биты падают.

То, что я считаю, я понял (например, то 5 >> 1же, 0101 >> 1что оценивает 0010), но я не вижу, как это работает с логическим значением? Я знаю , что имеет trueзначение 1и falseдля 0.

kiabbott
источник
3
Булево значение просто будет преобразовано в число, и тогда произойдет сдвиг, поэтому true= 1(десятичное) = 01(двоичное), сдвинутое влево на единицу, приведет к 10двоичному или 2десятичному.
ВЛАЗ
На самом деле, это решение неверно. В вышеупомянутых случаях это будет работать, но подумайте notNotNot(2, true): оно вернется false, но не верно ( !!true) должно быть true...
ФЗ
@FZs это правильно. Функция будет учитывать только нечетное число нотов на falseразрешении true.
ВЛАЗ
2
Я бы сказал notNotNot = (a,b) => !!((a%2)^b)вместо этого ...
ФЗ
Почему не просто (a, b) => !!((a + b) % 2)?
Джонас Уилмс

Ответы:

16

Функция, которую вы дали, не удовлетворяет требованиям. Сдвиг вправо не будет делать то, о чем просят. Например, ваше notNotNot(6,true)это false, неtrue положено через вашу функцию.

Ваш вопрос касается побитовой операции над логическим значением. Поскольку операторы любят >>и <<работают с целыми числами, Javascript сначала преобразует логическое значение в целое число. Так trueстановится 1 и falseстановится 0. Чтобы увидеть это, вы можете сдвинуться на ноль:

console.log("true is",true >> 0)
console.log("false is", false >> 0)
Таким образом, побитовая операция с логическими значениями - это просто побитовая операция с 0 или 1.

Использование !!- это удобный способ конвертировать что-либо в логическое значение. Он принимает все , что бы считать эквивалентом к ложным (например, 0, null, undefinedили «») и возвращает false. Точно так же все, что является правдивым (как 14, "привет", [4], {a: 1}) и вернуть true. !!работает, потому что первый восклицательный знак дает «не» выражения, которое всегда trueили false, тогда второй восклицательный знак дает противоположность этому ( falseилиtrue ).

Возвращаясь к задаче, он хочет применить не-оператор 'a' раз и сравнить со значением 'b'. Так что-то вроде этого будет работать:

function notNotNot(a, b) { return !!(a%2 - b); }
console.log("notNotNot(1, true)",notNotNot(1, true));
console.log("notNotNot(2, false)",notNotNot(2, false));
console.log("notNotNot(6, true)",notNotNot(6, true));

Всегда учусь
источник
9
Я думаю, что «вызов» действительно хочет, чтобы вы поняли, что вы можете легко сделать это без петли ...
BlueRaja - Дэнни Пфлугхофт
2
@ BlueRaja-DannyPflughoeft действительно. Самый простой способ сделать это без цикла - if (a % 2 === 1) return !b else return bно, как показано в других ответах, есть способы сделать это без разветвлений или циклов.
ВЛАЗ
да, но вопрос был не в том, чтобы выполнить задачу, а в том, как использовать побитовые операторы и логические значения. Но я обновил свой ответ не-циклично и не-если.
Всегда
14

Побитовые операторы всегда преобразуют свои операнды в целое число. Таким образом, 4 >> trueэто то же самое, 4 >> 1что будет делать сдвиг вправо на одну позицию

(decimal) 4 = (binary) 100

(binary) 100 >> 1 = (binary) 010

(binary) 010 = (decimal) 2

console.log(4 >> true);

Таким образом, использование trueили falseпросто окольный способ использования 1или 0.

notNotNotФункция имеет очень простую операцию, в целом:

  1. a%2преобразует первое число в 0четное или 1нечетное.
  2. >> bсдвиг вправо либо на 0позиции для falseили 1позиции дляtrue .
    • aнечетно (1) , и bэто false=1
      • смещений вправо нет, поэтому число остается неизменным.
    • aнечетно (1) , и bэто true=0
      • единственный установленный бит 1сдвигается вправо и отбрасывается.
    • aдаже (0) , и bэто false=0
      • смещений вправо нет, поэтому число остается неизменным.
    • aдаже (0) , и bэто true=0
      • базовое число 0не имеет битов, поэтому сдвиг вправо на любую величину не изменит его.
  3. !!() преобразует результат в логическое значение.

С учетом сказанного, решение здесь неправильное, поскольку notNotNot(2, true)будет производить false- aесть и bесть true. Ожидается, что он будет производить trueс тех пор !!true = true. Такая же проблема присутствует для любого четного числа и true.

Это можно легко исправить, используя битовое XOR вместо правого смещения:

  • aнечетно (1) , и bэто false=1
    • оба совпадают, поэтому они перевернуты 0
  • aнечетно (1) , и bэто true=0
    • они не совпадают, поэтому мы получаем 1
  • aдаже (0) , и bэто false=0
    • оба совпадают, поэтому мы получаем 0
  • aдаже (0) , и bэто true=1
    • они не совпадают, поэтому мы получаем 1

notNotNot = (a,b) => !!(a%2 ^ b);

console.log("!!true = ", notNotNot(2, true))
console.log("!!!true =", notNotNot(3, true))
console.log("!!false = ", notNotNot(2, false))
console.log("!!!false = ", notNotNot(3, false))

//bonus
console.log("true = ", notNotNot(0, true))
console.log("false = ", notNotNot(0, false))

Просто для полноты, на случай, если вы хотите полностью побитовую операцию:

Операция по модулю %2может быть изменена на побитовое И &1получить младший бит. Для четных чисел это даст, 0так как вы будете вычислять

xxx0
&
0001

который равен нулю. И для нечетных чисел то же самое относится, но вы получите один в результате:

xxx1
&
0001

Так что результаты a&1и a%2идентичны. Кроме того, хотя побитовые операции преобразуют число в 32-разрядное целое число со знаком, что не имеет значения, так как четность будет сохранена.

VLAZ
источник
4

Во-первых, (a,b) => !!(a%2 >> b)не совпадает с результатами примеров. Я разобью именно то, что он делает, используя notNotNot(6, true) ➞ true.

  • Кулак a%2, просто получи aделение на 2, верни остаток. Таким образом, мы получим 0 для четного числа и 1 для нечетного числа.a = 6 a%2 = 0в этом случае.
  • Затем 0 >> bсдвиньте 1 число справа, потому что, как вы сказали, trueоценивается как 1. Итак, мы получаем 0 >> 1 = 0.
  • Последнее !!(0)просто и может быть разбито так !0 = true, тогда !true = false.

Так что, если мы думаем об этом до тех пор , как bэто true, мы всегда будем получать возвращены false. Допустим, мы имеем = 5, Ь = верно вычисляемые 5%2 = 1, 1 >> 1 = 0. Вы можете видеть из-за мода (%2 ) у нас будет только 1 или 0 (только когда-либо будет 1 цифра), и true будет всегда сдвигаться от 1, когда он у нас есть.

Простой способ взглянуть на эту проблему подобен isEvenOrNotфункции. Таким образом, aчисло, которое мы проверяем, bявляется логическим для проверки, является ли оно четным (true) или нет (false). Это работает, потому что каждая notдобавленная секунда будет правдой.

Таким образом, решение с использованием побитового может быть что - то вроде: (a,b) => !!(a&1 ^ b). Я позволю вам весело разобраться, почему это работает! :)

Еще немного о том, как объяснить, как сдвиг работает с логическим значением. Таким образом, trueкак вы сказали, будет 1, а false будет 0. Итак, как показано в вашем примере, 0101 >> trueэто то же самое, что и 0101 >> 1.

Надеюсь, это поможет.

Я использовал следующее в качестве ссылки для побитового: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators

domsim1
источник
1
(a%2)  //ignore all but the least significant bit (LSB)
(a%2 >> b )  //if TRUE,  shifts right, resolves to 0
               //if FALSE, no shift,     resolves to LSB

// 0 and LSB are both integers so convert to boolean by using logical/boolean NOT
!(a%2 >> b ) //resolves to the boolean which it is NOT
!!(a%2 >> b ) //resolves to the boolean which it is NOT NOT

NB. Для любого логического значения четное число NOT приводит к исходному логическому значению, а нечетное число NOT приводит к противоположному логическому значению.

Младший бит любого числа определяет, является ли число нечетным или четным (0 четным, 1 нечетным).

Стивен Даффи
источник
1

Я вижу, что ваша задача:

/* Create a function where given n number of "not",
   evaluate whether it's true or false.*/

Я не знаю, почему вы пишете notnotnotдля меня функцию, а не то, что просит задача.

Таким образом, в соответствии с задачей я сделал эту функцию not которая принимает ряд «неимущих» и оценивает их.

Первый путь

function not(n) {
  return Boolean(n - n - 1);
}

Второй способ с использованием XOr (^)

function not(n) {
  return Boolean(bool ^ (bool - 1));
}

Третий способ, используя Mod (%), указанный @VLAZ

function not(n) {
  return Boolean(n % 2);
}

Четвертый способ с использованием побитового А (&)

function not(n) {
  return Boolean(n & 1);
}

Тестовое задание

not(0)
//> false 
not(1)
//> true
not(2)
//> false
not(515)
//> true
SaymoinSam
источник
1
Манипулирование битами здесь полезно, так как мы не заботимся о количестве n- только о том, является ли оно четным или нечетным, так как любые два NOT отменяют, так !!!!!bже как и !b. Следовательно, нам не нужен цикл, если мы просто возьмем n%2- мы бы получили 1за НЕ и 0за «оставь это прежним». Поскольку у нас есть число, мы можем просто выполнять побитовые операции.
ВЛАЗ
Ну, я не думал об этом, но вы правы, мы имеем дело только с 0 и 1, спасибо, это полезный комментарий :)
SaymoinSam
0

Давайте сначала проанализируем решение

notNotNot(oddNumber, true)   false
notNotNot(evenNumber, true)  true

notNotNot(oddNumber, false)   true
notNotNot(evenNumber, false)  false

Теперь анализ для (a,b) => !!(a%2 >> b)

a%2 == 0  even number
a%2 == 1  odd number

// For a%2 == 0

a%2 >> b  if b is true   0 >> 1  0   // Not working
a%2 >> b  if b is false  0 >> 0  0


// For a%2 == 1

a%2 >> b  if b is true   1 >> 1  0
a%2 >> b  if b is false  1 >> 0  1

Thats означает , что это не работает для notNotNot(6, true)это , trueно текущее решение дает false.

Мы можем ^(XOR) оператор, чтобы это исправить(a,b) => !!(a%2 ^ b)

Теперь анализ для (a,b) => !!(a%2 ^ b)

a%2 == 0  even number
a%2 == 1  odd number

// For a%2 == 0

a%2 ^ b  if b is true   0 ^ 1  1   // Now working
a%2 ^ b  if b is false  0 ^ 0  0


// For a%2 == 1

a%2 ^ b  if b is true   1 ^ 1  0
a%2 ^ b  if b is false  1 ^ 0  1

!(a%2 ^ b) use `!` to make int as boolean but solution result will reversed then
!!(a%2 ^ b) use `!` again to reversed it again and make it correct.

Пример:

notNotNot = (a,b) => !!(a%2 ^ b);

console.log("!!!!true = ", notNotNot(4, true))
console.log("!!!!false = ", notNotNot(4, false))
console.log("!!!true =", notNotNot(3, true))
console.log("!!!false = ", notNotNot(3, false))

Эклавия
источник