"Prestar un poco" dos números

20

¿Sabía que un número pequeño puede tomar prestados bits de un número mayor? Aquí hay un ejemplo. Digamos nuestros dos números 5 y 14. Primero, escríbalos en binario:

5       14
000101  001110

En primer lugar tomamos el más pequeño en poco lejos del mayor número, y se lo damos a los más pequeños fuera poco en el otro número. Entonces

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

Ahora tenemos

000111  001100

y nuestros números son 7 y 12. El primer número es aún más pequeño, así que continuamos.

000111  001100
001111  001000

Ahora tenemos 15 y 8, para que podamos parar. Llamaremos a este conjunto de operaciones "préstamo de bits" dos números. Hagamos otro ejemplo. 20 y 61.

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

Entonces nuestro resultado final es 32, 63. Hagamos uno más. 31 y 12. 31 ya es más grande que 12, ¡así que no hay nada que hacer! El préstamo de bits 31 y 12 da 31 y 12, sin cambios.

El reto

Su desafío es escribir un programa o función que tome dos números y los tome prestados. Los dos números siempre serán enteros positivos. Su entrada y salida puede estar en cualquier formato razonable.

Prueba IO:

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

Se aplican lagunas estándar, ¡y la respuesta más corta en bytes gana!

DJMcMayhem
fuente

Respuestas:

12

Jalea , 11 bytes

~1¦&N$^µ</¿

Pruébalo en línea! o verificar todos los casos de prueba .

Antecedentes

Podemos extraer el último bit establecido de un entero n de la siguiente manera.

n + 1 alterna todos los bits del conjunto final de n y el bit adyacente no establecido Por ejemplo, 10011 2 + 1 = 10100 2 .

Dado que ~ n = - (n + 1) = -n - 1 , -n = ~ n + 1 , entonces -n aplica lo anterior al NOT bit a bit de n (que alterna todos los bits), alternando todos los bits antes del último 1 .

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

Al tomar n y -n el AND bit a bit de n y -n todos los bits antes de la última bit conjunto se anulan (desde desigual en n y -n ), produciendo de este modo el último bit conjunto de n .

Por ejemplo, 10100 2 y -10100 2 = 10100 2 y 01100 2 = 00100 2 .

Por lo tanto, XORing n con n & -n desactiva el último bit establecido de n .

Por el contrario, para desarmar el último bit establecido de n , es suficiente aplicar lo anterior a ~ n , de donde derivamos la fórmula n ^ (~ n & - ~ n) .

Cómo 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
fuente
6

J, 31 26 bytes

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

Enfoque directo usando recursividad y trucos bit a bit. Para desactivar (establecer a 0 ) el bit más a la derecha en ( 1 ) para un valor n , puede realizar bit a bit y entre n y n -1, y activar (establecer a 1 ) el más a la derecha apagado ( 0 ) bits para un valor de n , se pueden realizar a nivel de bit-o entre n y n + 1.

Uso

La entrada consta de dos enteros, uno aplicado en el LHS y el otro en el RHS, y la salida es una lista de los valores prestados por bit.

   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

Explicación

,`(($:~(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
millas
fuente
¡Buena respuesta! Lamento cambiar el desafío después de haber publicado una respuesta, pero he simplificado un poco el desafío. (ya no necesita iterar sobre la lista). Eso debería permitirle acortarlo un poco más.
DJMcMayhem
@DrGreenEggsandIronMan J en realidad está aplicando la función por elementos entre las dos matrices sin ninguna clasificación explícita, lo cual es bueno. A menos que haya otro truco, probablemente se mantendrá igual.
millas
4

Python, 42 bytes

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

¡Gracias a @ jimmy23013 por jugar golf 4 bytes! ¡Gracias a @LeakyNun por jugar 2 bytes!

Pruébalo en Ideone .

Dennis
fuente
3

Mathematica, 46 bytes

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

El mismo método utilizado en mi solución en J.

Gracias a @ Martin por guardar 1 byte y recordarme la aplicación infix ~.

Uso

La entrada consta de dos argumentos enteros y la salida es una lista con los valores prestados en bits.

Ejemplo

millas
fuente
Pensé que intentaría algo divertido, pero desafortunadamente es un byte más largo: #//.{x_,y_}/;x<y:>{BitOr[x,x+1],BitAnd[y,y-1]}&(aunque quizás tengas una idea de cómo acortarlo)
Martin Ender
Esa es una regla clara, pero no estoy muy familiarizado con las reglas del golf. Por lo general, solo uso la sustitución /.y la condición /;. Wish Mathematica podría cambiar entre booleano y bit a bit al inspeccionar los tipos de argumento &&y tal.
millas
3

Pyth, 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       <- El cambio de entrada / salida formato
 mW <FH.exb ^ 2x_ + 0jb2kZ 
m.W <FH.U ,. | bhb. & ZtZZ 
.W <FH.U ,. | bhb. & ZtZZ          <- formato de entrada / salida
 modificado .W <FH.U ,. | bhb. y ZtZ
.W <FH.U ,. | bhb. & T

Banco de pruebas.

Monja permeable
fuente
Lamento cambiar el desafío después de haber publicado una respuesta, pero he simplificado un poco el desafío. (ya no necesita iterar sobre la lista). Aunque afortunadamente eso te permitirá acortarlo.
DJMcMayhem
@DrGreenEggsandIronMan Solo guardó un byte. Pyth es así de eficiente.
Leaky Nun
2

Laberinto , 37 34 bytes

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

Pruébalo en línea!

Explicación

Imprimación de laberinto rápido:

  • Labyrinth opera en dos pilas de enteros de precisión arbitraria, principal y auxiliar , que inicialmente se llenan con una cantidad infinita (implícita) de ceros.
  • El código fuente se asemeja a un laberinto, donde el puntero de instrucción (IP) sigue los corredores. Todo el flujo de control interesante ocurre en las uniones: cuando la IP tiene más de una celda a la que ir, se inspecciona la parte superior de la pila principal. Si el valor es negativo, la IP gira a la izquierda, si es positiva, la IP gira a la derecha y, de lo contrario, se mueve hacia adelante. Si la dirección elegida está bloqueada por un muro (es decir, un espacio), la IP se mueve en la dirección opuesta.

El programa usa el mismo algoritmo que las otras respuestas: reemplazamos (a, b)con (a | a+1, b & b-1)tanto tiempo como a < b. Agregaré una explicación completa después de haber intentado jugar al golf un poco más.

La IP comienza en la esquina superior izquierda, hacia la derecha. ?lee un entero a. Entonces "es un no-op, pero es necesario evitar que la IP baje inmediatamente. Esto también es un callejón sin salida, por lo que la IP se da vuelta y se ejecuta ?nuevamente para leer b. }luego se mueve bde main a aux , así que ahora tenemos:

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

La |continuación no hace nada, ya que lleva el operador OR de ay 0. Como sabemos aque siempre es positivo, la IP gira hacia el este (porque no puede girar hacia el oeste). Esto comienza el ciclo principal del programa. Comenzamos con una sección lineal corta para comparar ay 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.

La IP está ahora en otra unión. Primero, consideremos el caso donde el resultado es positivo. Eso significa b > ay tenemos que realizar una iteración más. Esa iteración también es completamente lineal. Tenga en cuenta que las pilas son actualmente:

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.

Y luego volvemos al inicio del ciclo (dado aque nuevamente es positivo, la IP vuelve a girar hacia el este).

Si en algún momento b-aya no es positivo, la IP tomará una de las otras dos rutas. Tenga en cuenta que en ambos casos buscamos a ala {, después haga clic en una esquina donde la IP sigue la curva y luego imprimir acon !. Ahora la parte superior de la pila está nuevamente, lo b-aque significa que en ambos casos la IP terminará moviéndose hacia el este. Todo lo que queda es un bit lineal corto ahora:

;   Discard b-a.
\   Print a linefeed.
!   Print b.
@   Terminate the program.
Martin Ender
fuente
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);}

Sin golf y casos de prueba:

Pruébalo aquí

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

Salida:

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

Reglas del desafío de edad [ 126 125 123 bytes]:

NOTA: Las antiguas reglas de desafío usaban dos matrices de enteros como entrada, en lugar de dos enteros sueltos.

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
fuente
Lamento cambiar el desafío después de haber publicado una respuesta, pero he simplificado un poco el desafío. (ya no necesita iterar sobre la lista). Aunque afortunadamente eso te permitirá acortarlo.
DJMcMayhem
@DrGreenEggsandIronMan Editado. Por cierto, generalmente es una mala práctica cambiar las reglas después de que las personas hayan publicado sus respuestas ... Pero como dijiste, debería reducir el conteo de bytes, así que estoy de acuerdo con eso. (PD: No has hecho el comentario anterior sobre la respuesta de todos.)
Kevin Cruijssen
1
Puede reescribir su whileciclo de esta manerafor(;x<y;x|=x+1,y&=y-1);
cliffroot
Sé que lo es. -_-Desearía haberlo escrito mejor desde el principio. Afortunadamente no es un cambio irrazonable o drástico. Además, sí, no comenté cada respuesta, pero informé a cada usuario. No tenía ganas de informar al mismo usuario varias veces. No hice ningún comentario sobre la publicación de Dennis, pero eso se debe a que fue uno de los usuarios que me animó a cambiarla inicialmente.
DJMcMayhem
1

JavaScript (ES6), 33 bytes

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

Puerto simple de las respuestas de @miles.

Neil
fuente
Olvidaste el f=att al principio: P
Mama Fun Roll
1
Olvidó el "otra vez" ;-)
Neil
1

Julia, 27 bytes

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

Pruébalo en línea!

Cómo funciona

Definimos el operador binario <|para nuestros propósitos. No está definido en las versiones recientes de Julia, pero el analizador aún lo reconoce como operador. Si bien \(no está definido explícitamente para enteros) es un byte más corto, su alta prioridad requeriría reemplazarse x|-~x<|y&~-ypor(x|-~x)\(y&~-y) , aumentando así el número de bytes.

<|comprueba si su primer argumento es estrictamente menor que el segundo. Si es así, se llama recursivamente con argumentos x | - ~ x = x | (x + 1) y y & ~ -y = y & (y - 1) .

Dado que agregar 1 a x alterna todos los bits del conjunto final y el bit sin establecer más bajo, x | (x + 1) alterna el bit sin establecer más bajo (y ningún otro bit). Del mismo modo, dado que restando 1 de y alterna todos los bits no establecidos finales y el bit establecido más bajo, y & (y + 1) alterna el bit establecido más bajo.

Finalmente, cuando la desigualdad x <y ya no se mantiene, <|devuelve el par [x, y] .

Dennis
fuente
0

MATLAB, 67 66 bytes

lazo:

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

El mismo enfoque para cambiar los bits que muchas otras respuestas.

pajonk
fuente
0

Clojure, 63 bytes

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

Mismo método que el usado en mi solución en 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])
millas
fuente