Como a operação bit a bit funciona nos booleanos?

26

Me deparei com esse desafio no Edabit e não consegui resolver essa solução de operação bit a bit.

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

O desafio:

//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

Eu fiz algumas pesquisas que esse operador:

Desloca para a direita empurrando cópias do bit mais à esquerda da esquerda e deixe os bits mais à direita caírem.

Acho que entendi (por exemplo, o 5 >> 1mesmo 0101 >> 1que avalia 0010), mas não consigo ver como isso funciona com um booleano? Eu sei que trueavalia para 1e falsepara 0.

Kiabbott
fonte
3
Um booleano será transformado em um número e o deslocamento ocorrerá; portanto, true= 1(decimal) = 01(binário) deslocado para a esquerda por um produziria 10binário ou 2decimal.
VLAZ
Na verdade, esta solução está incorreta. Nos casos acima mencionados, ele funcionará, mas considere notNotNot(2, true): retornará false, mas não é verdadeiro ( !!true) deve ser true...
FZs
@ FZs está correto. A função considerará apenas um número ímpar de nots em um falsepara resolver true.
VLAZ
2
Eu diria que notNotNot = (a,b) => !!((a%2)^b)...
FZs
Por que não apenas (a, b) => !!((a + b) % 2)?
Jonas Wilms

Respostas:

16

A função que você deu não satisfaz o desafio. A mudança para a direita não fará o que é pedido. Por exemplo, você notNotNot(6,true)é false, não truequando realiza sua função.

Sua pergunta é sobre a operação bit a bit em um booleano. Como os operadores gostam >>e <<trabalham com números inteiros, o Javascript primeiro converte o valor booleano em um número inteiro. Então, truetorna-se 1 e falsetorna - se 0. Para ver isso, você pode mudar por zero:

console.log("true is",true >> 0)
console.log("false is", false >> 0)
Portanto, operação bit a bit em booleanos é realmente apenas operação bit a bit em 0 ou 1.

Usar !!é uma maneira prática de converter qualquer coisa em um booleano. Ele pega qualquer coisa que seja considerada equivalente a false (como 0 null, undefinedou "") e devolve false. Da mesma forma, qualquer coisa que seja verdadeira (como 14, "olá", [4], {a: 1}) e devolva true. !!funciona porque o primeiro ponto de exclamação fornece o 'não' da expressão que é sempre trueou false, então o segundo ponto de exclamação fornece o oposto disso ( falseou true).

Voltando ao desafio, ele deseja aplicar o não operador 'a' vezes e comparar com o valor 'b'. Então, algo assim funcionaria:

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

Sempre aprendendo
fonte
9
Eu acho que o "desafio" realmente quer que você perceba que você pode fazer isso facilmente sem um loop ...
BlueRaja - Danny Pflughoeft
2
@ BlueRaja-DannyPflughoeft de fato. A maneira mais simples de fazer isso sem um loop é, if (a % 2 === 1) return !b else return bmas, como mostrado em outras respostas, existem maneiras de fazê-lo sem ramificações ou loops.
VLAZ
sim, mas a questão não era fazer o desafio - era sobre como operador bit a bit e valores booleanos. Mas atualizei minha resposta de maneira não-loop e não-if.
Sempre aprendendo
14

Operadores bit a bit sempre convertem seus operandos em um número inteiro. Então, 4 >> trueé o mesmo 4 >> 1que fará um pequeno deslocamento certo em uma posição

(decimal) 4 = (binary) 100

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

(binary) 010 = (decimal) 2

console.log(4 >> true);

Portanto, usar trueor falseé apenas uma maneira indireta de usar 1or 0.

A notNotNotfunção possui uma operação muito simples, em geral:

  1. a%2converte o primeiro número em 0para par ou 1ímpar.
  2. >> bmuda para a direita, quer por 0posições falseou 1por true.
    • aé ímpar (1) e bé false=1
      • há zero turnos para a direita, portanto o número permanece o mesmo.
    • aé ímpar (1) e bé true=0
      • o único bit definido 1é deslocado para a direita e descartado.
    • aé par (0) e bé false=0
      • há zero turnos para a direita, portanto o número permanece o mesmo.
    • aé par (0) e bé true=0
      • o número base é o 0que não possui bits definidos; portanto, mudar a quantidade certa para a direita não o altera.
  3. !!() converte o resultado em booleano.

Com isso dito, a solução aqui está errada, pois notNotNot(2, true)produzirá false- aé uniforme e bé true. A expectativa é que ela produza truedesde então !!true = true. O mesmo problema está presente para qualquer número par e true.

Pode ser facilmente corrigido usando o XOR bit a bit em vez do deslocamento à direita:

  • aé ímpar (1) e bé false=1
    • ambos combinam, então eles são virados para 0
  • aé ímpar (1) e bé true=0
    • eles não combinam, então temos 1
  • aé par (0) e bé false=0
    • ambos combinam, então temos 0
  • aé par (0) e bé true=1
    • eles não combinam, então temos 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))

Apenas por questões de integridade, caso você queira uma operação totalmente bit a bit:

A operação do módulo %2pode ser alterada para um bit a bit E &1obter o bit mais baixo. Para números pares, isso renderia 0uma vez que você estaria computando

xxx0
&
0001

que é zero. E para números ímpares, o mesmo se aplica, mas você obteria um como resultado:

xxx1
&
0001

Portanto, os resultados a&1e a%2são idênticos. Além disso, embora as operações bit a bit convertam o número em um número inteiro assinado de 32 bits que não importa, pois a paridade seria preservada.

VLAZ
fonte
4

Em primeiro lugar, (a,b) => !!(a%2 >> b)não corresponde aos resultados dos exemplos. Vou detalhar exatamente o que está fazendo usando notNotNot(6, true) ➞ true.

  • Punho a%2, basta adividir por 2 e retornar o restante. Portanto, obteremos 0 para um número par e 1 para um número ímpar. a = 6 a%2 = 0nesse caso.
  • Em seguida, 0 >> bdesloque 1 número da direita, porque, como você disse, trueavalia 1. Então chegamos 0 >> 1 = 0.
  • Última !!(0), é simples, e pode ser dividido da seguinte forma, !0 = truee, em seguida !true = false.

Portanto, se pensarmos sobre isso enquanto bfor true, sempre teremos retorno false. Vamos dizer que temos a = 5, b = true avaliar para 5%2 = 1, 1 >> 1 = 0. Você pode ver que, devido ao mod ( %2), teremos apenas 1 ou 0 (apenas 1 dígito) e true sempre desativará o 1 quando o tivermos.

Uma maneira simples de encarar esse problema é como uma isEvenOrNotfunção. Assim aé o número que estamos verificando e bé um booleano para verificar se é par (verdadeiro) ou não (falso). Isso funciona porque cada segundo notadicionado será verdadeiro.

Então, uma solução usando bit a bit poderia ser algo como: (a,b) => !!(a&1 ^ b). Vou deixar você se divertir ao descobrir por que funciona! :)

Um pouco mais sobre como o shift funciona com um booleano. Então, truecomo você disse, será 1 e false será 0. Portanto, como mostrado no seu exemplo, 0101 >> trueé o mesmo que 0101 >> 1.

Eu espero que isso ajude.

Usei o seguinte como referência para bit a bit: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators

domsim1
fonte
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 Para qualquer booleano, um número par de NOTs resulta no booleano original e um número ímpar de NOTs resulta no booleano oposto

O LSB de qualquer número determina se o número é ímpar ou par. (0 par, 1 ímpar)

Stephen Duffy
fonte
1

Vejo que sua tarefa é:

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

Não sei por que você está escrevendo uma notnotnotfunção para mim que não é o que a tarefa pede.

Então, de acordo com a tarefa, criei essa função not que aceita vários "nots" e os avalia.

A primeira maneira

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

A segunda maneira usando XOr (^)

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

A terceira maneira usando Mod (%) apontado por @VLAZ

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

A quarta maneira usando E bit a bit (&)

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

Teste

not(0)
//> false 
not(1)
//> true
not(2)
//> false
not(515)
//> true
SaymoinSam
fonte
11
A manipulação de bits é útil aqui, já que não nos importamos com a quantidade n- apenas se é par ou ímpar, já que dois NOTs não são cancelados, !!!!!bé o mesmo que !b. Portanto, não precisamos de um loop se apenas fizermos n%2- teríamos 1NOT e 0por "manteremos o mesmo". Como temos um número, podemos apenas fazer operações bit a bit.
VLAZ
Bem, eu não pensei nisso, mas você está certo, estamos lidando apenas com 0 e 1, obrigado, este é um comentário útil :)
SaymoinSam
0

Deixa a solução de análise primeiro

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

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

Agora analise o para (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 meios isto não está trabalhando para notNotNot(6, true)se truemas solução atual dá false.

Podemos ^operador (XOR) para corrigi-lo(a,b) => !!(a%2 ^ b)

Agora analise o para (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.

Exemplo:

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))

Eklavya
fonte