"Emprestar bit" dois números

20

Você sabia que um número pequeno pode emprestar bits de um número maior? Aqui está um exemplo. Digamos nossos dois números 5 e 14. Primeiro, escreva-os em binário:

5       14
000101  001110

Primeiro vamos dar a menor em pouco longe do maior número, e nós dar-lhe com o menor off pouco por outro número. tão

This bit turns off
            |
            v
000101  001110
    ^
    |
This bit turns on

Agora temos

000111  001100

e nossos números são 7 e 12. O primeiro número ainda é menor, por isso continuamos.

000111  001100
001111  001000

Agora temos 15 e 8, para que possamos parar. Vamos chamar esse conjunto de operações de "empréstimo de bits" de dois números. Vamos fazer outro exemplo. 20 e 61.

20        61
010100    111101
010101    111100
010111    111000
111111    100000
63        32

Portanto, nosso resultado final é 32, 63. Vamos fazer mais um . 31 e 12. 31 já é maior que 12, então não há nada a fazer! O empréstimo de bits 31 e 12 fornece 31 e 12, sem alterações.

O desafio

Seu desafio é escrever um programa ou função que aceite dois números e os empreste pouco. Os dois números sempre serão números inteiros positivos. Sua entrada e saída podem estar em qualquer formato razoável.

Teste de E / S:

Input: 2, 3
Output: 3, 2

Input: 3, 2
Output: 3, 2

Input: 8, 23
Output: 31, 0

Input: 42, 81
Output: 63, 0

Input: 38, 41
Output: 47, 32

Input: 16, 73
Output: 23, 0

Input: 17, 17
Output: 17, 17

As brechas padrão se aplicam e a resposta mais curta em bytes vence!

DJMcMayhem
fonte

Respostas:

12

Gelatina , 11 bytes

~1¦&N$^µ</¿

Experimente online! ou verifique todos os casos de teste .

fundo

Podemos extrair o último bit definido de um número inteiro n da seguinte maneira.

n + 1 alterna todos os bits finais de n e o bit não definido adjacente. Por exemplo, 10011 2 + 1 = 10100 2 .

Como ~ n = - (n + 1) = -n - 1 , -n = ~ n + 1 , então -n aplica o acima ao NOT bit a bit de n (que alterna todos os bits), alternando todos os bits antes do último 1 .

Por exemplo, -10100 2 = ~ 10100 2 + 1 = 01011 2 + 1 = 01100 2 .

Ao pegar n & -n, o AND bit a bit de n e -n todos os bits anteriores ao último bit definido são anulados (desde que desiguais em n e -n ), produzindo assim o último bit definido de n .

Por exemplo, 10100 2 e -10100 2 = 10100 2 e 01100 2 = 00100 2 .

Assim, XORing n com n & -n desativa o último bit definido de n .

Por outro lado, para desarmar o último bit definido de n , basta aplicar o acima a ~ n , de onde derivamos a fórmula n ^ (~ n & - ~ n) .

Como funciona

~1¦&N$^µ</¿  Main link. Argument: A (list of pairs)

          ¿  While loop:
        </     Condition: Reduce p by less-than. True iff x < y.
       µ       Body chain:
~1¦              Apply bitwise NOT to the x, first item of the pair.
     $           Convert the two links to the left into a monadic chain.
    N              Negate; multiply [~x, y] by -1, yielding [-~x, -y].
   &               Logical AND. Yields [-~x & ~x, -y & y].
      ^            Vectorized XOR with p. Yields [(-~x & ~x) ^ x, (-y & y) ^ y].
Dennis
fonte
6

J, 31 26 bytes

,`(($:~(OR>:))~(AND<:))@.<

Abordagem direta usando recursão e truques bit a bit. Para desativar (definido como 0 ) o bit mais à direita ativado ( 1 ) para um valor n , você pode executar bit a bit e entre n e n -1 e ativar (definido como 1 ) o mais à direita off ( 0 ) bit para um valor n , você pode executar bit a bit - ou entre n e n +1.

Uso

A entrada consiste em dois números inteiros, um aplicado no LHS e outro no RHS, e a saída é uma lista dos valores emprestados por bits.

   f =: ,`(($:~(OR>:))~(AND<:))@.<
   2 f 3
3 2
   3 f 2
3 2
   8 f 23
31 0
   42 f 81
63 0
   38 f 41
47 32
   16 f 73
23 0
   17 f 17
17 17

Explicação

,`(($:~(OR>:))~(AND<:))@.<  Input: x on LHS, y on RHS
                            If x < y,
,                             Form a 2-element array [x, y] and return
                            Else
                   <:         Decrement y
                AND           Perform bitwise-and on y and y-1, call it y'
          >:                  Increment x
        OR                    Perform bitwise-or on x and x+1, call it x'
    $:                        Call recursively on x' and y' and return
milhas
fonte
Boa resposta! Desculpe alterar o desafio depois que você postou uma resposta, mas simplifiquei o desafio um pouco. (você não precisa mais repetir a lista). Isso deve permitir que você diminua um pouco mais.
DJMcMayhem
O @DrGreenEggsandIronMan J está realmente aplicando a função elemento a elemento entre as duas matrizes sem nenhuma classificação explícita, o que é bom. A menos que haja outro truque, provavelmente continuará o mesmo.
milhas
4

Python, 42 bytes

f=lambda x,y:x<y and f(x|x+1,y&y-1)or(x,y)

Graças a @ jimmy23013 por jogar fora 4 bytes! Graças a @LeakyNun por jogar fora 2 bytes!

Teste em Ideone .

Dennis
fonte
3

Mathematica, 46 bytes

If[#<#2,BitOr[#,#+1]~#0~BitAnd[#2,#2-1],{##}]&

O mesmo método usado na minha solução em J.

Obrigado a @ Martin por salvar 1 byte e me lembrar do aplicativo infix ~.

Uso

A entrada consiste em dois argumentos inteiros e a saída é uma lista com os valores emprestados por bits.

Exemplo

milhas
fonte
Pensei em tentar algo engraçado, mas infelizmente é um byte mais: #//.{x_,y_}/;x<y:>{BitOr[x,x+1],BitAnd[y,y-1]}&(talvez você tenha uma idéia de como reduzi-lo embora)
Martin Ender
Essa é uma regra legal, mas eu não estou muito familiarizado com as regras do golfe. Normalmente, só uso substituição /.e condição /;. Gostaria que o Mathematica pudesse alternar entre booleano e bit a bit, inspecionando tipos de argumento para &&e tal.
milhas
3

Pitão, 29 27 25 22 21 20 19 18 16 bytes

MxG ^ 2x _ + \ 0.BG`HCm.W <FHgVZU2dC 
MxG ^ 2x_ + 0jG2HCm.W <FHgVZU2dC 
Cm.W <FH.bxN ^ 2x_ + 0jN2YZ2dC 
m.W <FH.bxN ^ 2x_ + 0jN2YZ2       - entrada alterada formato
 mW <FH.exb ^ 2x_ + 0jb2kZ 
m.W <FH.U,. | bhb. & ZtZZ 
.W <FH.U ,. | bhb. & ZtZZ          <- mudou o formato de entrada / saída
 .W <FH.U ,. | bhb. & ZtZ
.W <FH.U ,. | bhb. & T

Suíte de teste.

Freira Furada
fonte
Desculpe alterar o desafio depois que você postou uma resposta, mas simplifiquei o desafio um pouco. (você não precisa mais repetir a lista). Embora, felizmente, isso permita que você diminua.
DJMcMayhem
@DrGreenEggsandIronMan Ele salvou apenas um byte. Pyth é tão eficiente.
Leaky Nun
2

05AB1E, 16 15 bytes

[D`›#`D<&sD>~s‚

Experimente online

Emigna
fonte
2

Labirinto , 37 34 bytes

?"
}
|=:{:
)   }
: :;-{
=&( {!;\!@

Experimente online!

Explicação

Primer Labirinto Rápido:

  • O labirinto opera em duas pilhas de números inteiros de precisão arbitrária, principal e auxiliar , que são inicialmente preenchidos com uma quantidade infinita (implícita) de zeros.
  • O código fonte se assemelha a um labirinto, onde o ponteiro de instrução (IP) segue os corredores. Todo o fluxo de controle interessante acontece nas junções: quando o IP possui mais de uma célula, o topo da pilha principal é inspecionado. Se o valor for negativo, o IP vira à esquerda, se for positivo, o IP vira à direita e, caso contrário, segue em frente. Se a direção escolhida for bloqueada por uma parede (ou seja, um espaço), o IP se moverá na direção oposta.

O programa usa o mesmo algoritmo que as outras respostas: substituímos (a, b)com (a | a+1, b & b-1)enquanto a < b. Vou adicionar uma explicação completa depois de tentar jogar um pouco mais.

O IP começa no canto superior esquerdo, indo para a direita. ?lê um número inteiro a. Então "é um no-op, mas é necessário impedir que o IP caia imediatamente. Esse também é um beco sem saída, portanto o IP se vira e executa ?novamente para ler b. }passa bde main para aux , então agora temos:

Main [ ... 0 a | b 0 ...] Aux

O |então não faz nada, porque leva o OR bit a bit de ae 0. Como sabemos que asempre é positivo, o IP vira para o leste (porque não pode virar para o oeste). Isso inicia o loop principal do programa. Começamos com uma seção linear curta para comparar ae b:

=   Swap tops of stacks, i.e. swap a and b.
:   Duplicate b.
{   Pull a over to main.
:   Duplicate a.
}   Push one copy back to aux.
-   Compute b-a.

O IP está agora em outra junção. Primeiro, vamos considerar o caso em que o resultado é positivo. Isso significa b > ae precisamos executar mais uma iteração. Essa iteração também é completamente linear. Observe que as pilhas estão atualmente:

Main [ ... 0 b (b-a) | a 0 ...] Aux

;   Discard b-a.
:   Duplicate b.
(   Decrement.
&   Bitwise AND with b, clearing the least-significant 1.
=   Swap new b with old a.
:   Duplicate a.
)   Increment.
|   Bitwise OR with a, setting the least-significant 0.

E então voltamos ao início do loop (já que aé positivo novamente, o IP vira para leste novamente).

Se em algum momento b-anão for mais positivo, o IP seguirá um dos outros dois caminhos. Note-se que em ambos os casos que buscar acom {, em seguida, bateu um canto onde o IP segue a curva e, em seguida, imprimir acom !. Agora, a parte superior da pilha está novamente, o b-aque significa que, nos dois casos, o IP acabará se movendo para o leste. Tudo o que resta é um pequeno bit linear agora:

;   Discard b-a.
\   Print a linefeed.
!   Print b.
@   Terminate the program.
Martin Ender
fonte
1

Java 7, 73 bytes

void d(int x,int y){while(x<y){x|=x+1;y&=y-1;}System.out.print(x+","+y);}

Casos não testados e de teste:

Experimente aqui.

public class Main{
  static void d(int x, int y){
    while(x < y){
      x |= x + 1;
      y &= y - 1;
    }
    System.out.print(x + "," + y);
  }

  public static void main(String[] a){
    print(2, 3);
    print(3, 2);
    print(8, 23);
    print(42, 81);
    print(38, 41);
    print(16, 73);
    print(17, 17);
  }

  public static void print(int a, int b){
    d(a, b);
    System.out.println();
  }
}

Saída:

3,2
3,2
31,0
63,0
47,32
23,0
17,17

Regras de desafio antigas [ 126 125 123 bytes]:

NOTA: As regras de desafio antigas usavam duas matrizes inteiras como entrada, em vez de dois inteiros soltos.

void d(int[]a,int[]b){int i=-1,x,y;while(++i<a.length){x=a[i];y=b[i];for(;x<y;x|=x+1,y&=y-1);System.out.println(x+","+y);}}
Kevin Cruijssen
fonte
Desculpe alterar o desafio depois que você postou uma resposta, mas simplifiquei o desafio um pouco. (você não precisa mais repetir a lista). Embora, felizmente, isso permita que você diminua.
DJMcMayhem
@DrGreenEggsandIronMan Editado. Btw, geralmente é uma prática ruim alterar as regras depois que as pessoas postam suas respostas. Mas, como você disse, deve diminuir a contagem de bytes, então estou bem com isso. (PS: Você ainda não fez o comentário acima sobre a resposta de todos.)
Kevin Cruijssen
1
Você pode reescrever seu whileloop assimfor(;x<y;x|=x+1,y&=y-1);
cliffroot
Eu sei que é. -_-Eu gostaria de ter escrito melhor desde o começo. Felizmente, não é uma mudança irracional ou drástica. Além disso, sim, não comentei todas as respostas, mas informei todos os usuários. Não queria informar o mesmo usuário várias vezes. Não comentei no post de Dennis, mas isso foi porque ele foi um dos usuários que me incentivou a alterá-lo inicialmente.
DJMcMayhem
1

JavaScript (ES6), 33 bytes

f=(n,m)=>n<m?f(n|n+1,m&m-1):[n,m]

Porta simples das respostas por @miles.

Neil
fonte
Você esqueceu o f=ath no início: P
Mama Fun Roll
1
Você esqueceu o "novo" ;-)
Neil
1

Julia, 27 bytes

x<|y=x<y?x|-~x<|y&~-y:[x,y]

Experimente online!

Como funciona

Definimos o operador binário <|para nossos propósitos. Ele é indefinido nas versões recentes do Julia, mas ainda é reconhecido como operador pelo analisador. Embora \(não definido explicitamente para números inteiros) seja um byte mais curto, sua alta precedência exigiria a substituição x|-~x<|y&~-ypor (x|-~x)\(y&~-y), aumentando assim a contagem de bytes.

<|verifica se o primeiro argumento é estritamente menor que o segundo. Nesse caso, ele se chama recursivamente com argumentos x | - ~ x = x | (x + 1) e y & ~ -y = y & (y - 1) .

Como a adição de 1 a x alterna todos os bits finais à direita e o menor bit não definido, x | (x + 1) alterna o bit não definido mais baixo (e nenhum outro bit). Da mesma forma, como subtrair 1 de y alterna todos os bits não definidos à direita e o bit mais baixo definido, y & (y + 1) alterna o bit mais baixo definido.

Finalmente, quando a desigualdade x <y não mais se mantém, <|retorna o par [x, y] .

Dennis
fonte
0

MATLAB, 67 66 bytes

loop:

function[]=f(x,y)
while x<y
x=bitor(x,x+1);y=bitand(y,y-1);end
x,y

recursivo (67 bytes):

function[]=f(x,y)
if x<y
f(bitor(x,x+1),bitand(y,y-1))
else
x,y
end

A mesma abordagem para alterar bits como muitas outras respostas.

pajonk
fonte
0

Clojure, 63 bytes

#(if(< % %2)(recur(bit-or %(inc %))(bit-and %2(dec %2)))[% %2])

O mesmo método usado na minha solução em J.

Uso

=> (def f #(if(< % %2)(recur(bit-or %(inc %))(bit-and %2(dec %2)))[% %2]))
=> (f 38 41)
[47 32]
=> (map (partial apply f) [[2 3] [3 2] [8 23] [42 81] [38 41] [16 73] [17 17]])
([3 2] [3 2] [31 0] [63 0] [47 32] [23 0] [17 17])
milhas
fonte