«Біт-позичайте» два числа


20

Чи знали ви, що невелика кількість може позичити біти з більшої кількості? Ось приклад. Скажімо, наші два числа 5 і 14. Спочатку запишіть їх у двійкові:

5       14
000101  001110

Спочатку ми беремо найменше на біт від більшого числа, і ми даємо його найменший від біта на інший номер. Так

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

Зараз у нас є

000111  001100

а наші числа - 7 і 12. Перше число все ж менше, тому ми продовжуємо.

000111  001100
001111  001000

Зараз у нас 15 і 8, тож ми можемо зупинитися. Ми будемо називати цей набір операцій «бітовим запозиченням» два числа. Зробимо ще один приклад. 20 та 61.

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

Отже наш кінцевий результат - 32, 63. Зробимо ще один . 31, і 12. 31 вже більший за 12, тому нічого робити! Біт-позики 31 і 12 дають 31 і 12, без змін.

Змагання

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

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

Застосовуються стандартні лазівки, і найкоротша відповідь у байтах виграє!

Відповіді:


12

Желе , 11 байт

~1¦&N$^µ</¿

Спробуйте в Інтернеті! або перевірити всі тестові випадки .

Фон

Ми можемо витягнути останній множиний біт цілого числа n таким чином.

n + 1 перемикає всі біти останнього набору n та сусідній нерозбірний біт. Наприклад, 10011 2 + 1 = 10100 2 .

Оскільки ~ n = - (n + 1) = -n - 1 , -n = ~ n + 1 , то -n застосовує вищезгадане до порозрядного НЕ з n (яке перемикає всі біти), тим самим перемикаючи всі біти до останнього 1 .

Наприклад, -10100 2 = ~ 10100 2 + 1 = 01011 2 + 1 = 01100 2 .

Приймаючи n & -n порозрядне AND з n і -n, всі біти до останнього встановленого біта зводяться до нулю (оскільки неоднакові в n і -n ), таким чином, виходить останній встановлений біт n .

Наприклад, 10100 2 & -10100 2 = 10100 2 & 01100 2 = 00100 2 .

Таким чином, XORing n з n & -n скасовує останній встановлений біт n .

І навпаки, для скидання останнього встановленого біта n достатньо застосувати вищезазначене до ~ n , звідки ми отримуємо формулу n ^ (~ n & - ~ n) .

Як це працює

~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].

6

J, 31 26 байт

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

Прямий підхід з використанням рекурсії та бітових трюків. Для того, щоб вимкнути (встановити на 0 ) біт правого значення ( 1 ) для значення n , ви можете виконувати порозрядно - і між n і n -1, і ввімкнути (встановити на 1 ) саме праворуч вимкнений ( 0 ) біт для значення n , ви можете виконувати порозрядну або між n і n +1.

Використання

Вхід складається з двох цілих чисел, одне з яких застосовується на LHS, а друге на RHS, а вихід - це список бітових запозичених значень.

   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

Пояснення

,`(($:~(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

Гарна відповідь! Вибачте, що змінили виклик після публікації відповіді, але я трохи спростив виклик. (вам більше не потрібно повторювати список). Це дозволить вам трохи скоротити його.
DJMcMayhem

@DrGreenEggsandIronMan J насправді застосовує функціональний елемент між двома масивами без явного ранжирування, що добре. Якщо немає іншого фокусу, він, ймовірно, залишиться таким же.
миль

4

Пітон, 42 байти

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

Дякуємо @ jimmy23013 за те, що виграли 4 байти! Дякуємо @LeakyNun за те, що виграли 2 байти!

Перевірте це на Ideone .


3

Математика, 46 байт

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

Той самий метод, який використовується в моєму розчині Дж.

Дякую @ Martin за збереження 1 байта та нагадування про додаток infix ~.

Використання

Вхід складається з двох цілих аргументів, а вихід - список зі значеннями, запозиченими бітами.

Приклад


Думав, я спробую щось смішне, але, на жаль, це байт довше: #//.{x_,y_}/;x<y:>{BitOr[x,x+1],BitAnd[y,y-1]}&(можливо, у вас є ідея, як скоротити це)
Мартін Ендер,

Це чітке правило, але я не дуже знайомий з правилами гольфу. Зазвичай я використовую лише заміну /.та стан /;. Побажання Mathematica може перемикатися між булевими та бітовими, перевіряючи типи аргументів на &&та інше.
миль

3

Піта, 29 27 25 22 21 20 19 18 16 байт

MxG ^ 2x _ + \ 0.BG`HCm.W <FHgVZU2dC 
MxG ^ 2x_ + 0jG2HCm.W <FHgVZU2dC 
Cm.W <FH.bxN ^ 2x_ + 0jN2YZ2dC 
m.W <FH.bxN ^ 2x_ + 0jNZZ       вихідний формат
 mW <FH.exb ^ 2x_ + 0jb2kZ 
m.W <FH.U,. | bhb. & ZtZZ 
.W <FH.U ,. | bhb. & ZtZZ          <- змінений формат вводу / виводу
 W <FH.U ,. | bhb. & ZtZ
.W <FH.U,. | Bhb. & T

Тестовий набір.


Вибачте, що змінили виклик після публікації відповіді, але я трохи спростив виклик. (вам більше не потрібно повторювати список). Хоча, на щастя, це дозволить скоротити.
DJMcMayhem

@DrGreenEggsandIronMan Це зберегло лише один байт. Піт - це так ефективно.
Лина монашка


2

Лабіринт , 37 34 байт

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

Спробуйте в Інтернеті!

Пояснення

Швидкий лабіринтний праймер:

  • Лабіринт працює на двох стеках цілих чисел з довільною точністю, головною та допоміжною , які спочатку заповнюються (неявною) нескінченною кількістю нулів.
  • Вихідний код нагадує лабіринт, де вказівник інструкції (IP) слідує за коридорами. Весь цікавий потік управління відбувається на стиках: коли в IP є більше однієї комірки, на яку потрібно перейти, перевіряється верхня частина основного стека. Якщо значення негативне, IP повертає ліворуч, якщо воно додатне, IP повертає праворуч, інакше воно рухається прямо вперед. Якщо обраний напрямок блокується стіною (тобто пробілом), IP замість цього рухається у зворотному напрямку.

Програма використовує той же алгоритм, що й інші відповіді: ми замінюємо їх (a, b)до (a | a+1, b & b-1)тих пір, як a < b. Я додам повне пояснення після того, як я ще спробував гольф.

IP починається у верхньому лівому куті, рухаючись праворуч. ?читає ціле число a. Тоді "безперебійний доступ, але потрібно не допустити негайного перенесення IP-адреси вниз. Це теж глухий кут, тому IP обертається і ?знову виконується для читання b. }потім переходить bвід основного до допоміжного , так що тепер у нас є:

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

|Тоді нічого не робить, тому що він приймає побітового АБО від aі 0. Оскільки ми знаємо, що aце завжди позитивно, то ІР повертає на схід (бо не може повернути на захід). З цього починається головний цикл програми. Почнемо з короткого лінійного перерізу, щоб порівняти aта 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.

Зараз IP знаходиться на іншому стику. Спочатку розглянемо випадок, коли результат позитивний. Це означає, що b > aнам потрібно виконати ще одну ітерацію. Ця ітерація також абсолютно лінійна. Зауважте, що стеки наразі:

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.

І тоді ми повертаємося до початку циклу (оскільки aзнову позитивний, IP знову повертає на схід).

Якщо в якийсь момент b-aбільше немає позитиву, IP пройде один з двох інших шляхів. Зверніть увагу , що в обох випадках ми забираємо aз {, а потім врізався в кут , де IP слід вигин , а потім роздрукувати aз !. Тепер знову вершина стека, а це b-aозначає, що в обох випадках IP в кінцевому підсумку рухатиметься на схід. Залишилося лише короткий лінійний біт:

;   Discard b-a.
\   Print a linefeed.
!   Print b.
@   Terminate the program.

1

Java 7, 73 байти

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

Невикористані та тестові справи:

Спробуйте тут.

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

Вихід:

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

Старі правила виклику [ 126 125 123 байт]:

ПРИМІТКА. Старі правила виклику використовували два цілі масиви як вхідні дані замість двох вільних цілих чисел.

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

Вибачте, що змінили виклик після публікації відповіді, але я трохи спростив виклик. (вам більше не потрібно повторювати список). Хоча, на щастя, це дозволить скоротити.
DJMcMayhem

@DrGreenEggsandIronMan Відредаговано. До речі, зазвичай погана практика змінювати правила після того, як люди опублікували свої відповіді. Але, як ви вже сказали, це має знизити кількість байтів, тому я з цим добре. (PS: Ви ще не коментували відповідь на відповідь усіх.)
Кевін Круїйсен

1
Ви можете переписати whileцикл такfor(;x<y;x|=x+1,y&=y-1);
cliffroot

Я знаю, що це так. -_-Я б хотів, щоб я написав це краще з самого початку. На щастя, це не необґрунтована або кардинальна зміна. Також так, я не коментував кожну відповідь, але повідомив кожного користувача. Мені не хотілося кілька разів повідомляти одного і того ж користувача. Я не коментував пост Денніса, але це тому, що він був одним із користувачів, який спонукав мене змінити його спочатку.
DJMcMayhem

1

JavaScript (ES6), 33 байти

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

Простий порт відповідей від @miles.


Ви забули f=спортсм початку: P
Mama Fun Roll

1
Ви забули "знову" ;-)
Ніл

1

Юлія, 27 байт

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

Спробуйте в Інтернеті!

Як це працює

Ми визначаємо двійковий оператор <|для наших цілей. Він не визначений в останніх версіях Джулії, але все ще визнаний парсером як оператор. Хоча \(не визначено явно для цілих чисел) на один байт коротше, його високий пріоритет потребує заміни x|-~x<|y&~-yна (x|-~x)\(y&~-y), таким чином збільшуючи кількість байтів.

<|перевіряє, чи не є його перший аргумент строго меншим, ніж другий. Якщо так, то рекурсивно називає себе аргументами x | - ~ х = х | (x + 1) і y & ~ -y = y & (y - 1) .

Оскільки додавання від 1 до x перемикає всі біти останнього набору та найнижчий скид, x | (х + 1) перемикає найнижчий невідомий біт (і жодних інших бітів). Аналогічно, оскільки віднімання 1 від y перемикає всі остаточні біти і найнижчий встановлений біт, y & (y + 1) перемикає найнижчий встановлений біт.

Нарешті, коли нерівність x <y вже не дотримується, <|повертає пару [x, y] .


0

MATLAB, 67 66 байт

петля:

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

рекурсивний (67 байт):

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

Той самий підхід до зміни бітів, як у багатьох інших косинок.


0

Clojure, 63 байти

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

Той самий метод, що і в моєму розчині Дж.

Використання

=> (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])
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.