Як рівне число?


47

У стародавніх греків такі речі називались поодинокими і подвійними парними числами. Приклад одиничного парного числа - 14. Він може бути розділений на 2 один раз, і в цей момент став непарним числом (7), після чого воно вже не ділиться на 2. Подвійне парне число - 20. Його можна розділити на 2 вдвічі, а потім стає 5.

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

Тестові приклади:

14 -> 1

20 -> 2

94208 -> 12

7 -> 0

-4 -> 2

Відповідь з найменшими байтами виграє.

Порада: Спробуйте перетворити число в базу 2. Подивіться, що вам каже.


11
@AlexL. Ви також можете подивитися, що це ніколи не стає дивним, так нескінченно рівним. Я міг би зберегти кілька байт, якщо дозволено переповнення стека;)
Geobits

1
The input will be a nonzero integerЧи потрібно це редагувати після вашого коментаря про те, що нуль є потенційним вкладом?
трихоплакс

2
Це називається 2-адичним оцінкою або 2-адичним порядком.
Павло

7
До речі, згідно з Вікіпедією, p-адична оцінка 0 визначається як нескінченність.
Павло

3
Яке дивне запитання!
corsiKa

Відповіді:


23

Желе , 4 байти

Æfċ2

В останній версії Jelly працює ÆEḢ(3 байти).

Æf      Calculate the prime factorization. On negative input, -1 appended to the end.
  ċ2    Count the 2s.

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


Це працює і для негативного введення.
lirtosiast

1
@ThomasKwa Я не думаю, що це має значення. Може бути, мета питання?
orlp

Не ÆEÆ добре? Він фактично виводить 0 для непарних чисел.
busukxuan

@busukxuan Це не працює для ± 1.
lirtosiast

1
@Tyzoid Jelly за замовчуванням використовує власну кодову сторінку в офлайн-інтерпретаторі, в якій один знак - один байт.
lirtosiast

93

машинний код x86_64, 4 байти

Інструкція BSF (бітове сканування вперед) робить саме це !

0x0f    0xbc    0xc7    0xc3

У зборах у стилі gcc це:

    .globl  f
f:
    bsfl    %edi, %eax
    ret

Вхід подається в регістр EDI і повертається в регістр EAX відповідно до стандартних 64-бітових умов виклику .

Через двійкове кодування, що доповнює два, це працює як для -ve, так і для + ve чисел.

Крім того, незважаючи на документацію, що говорить: "Якщо вміст вихідного операнда дорівнює 0, вміст операнда призначення не визначений". , Я знаходжу в моєму Ubuntu VM, що вихід f(0)0.

Інструкції:

  • Збережіть вищезгадане як evenness.sі зберітьgcc -c evenness.s -o evenness.o
  • Збережіть такий тест-драйвер як evenness-main.cі компілюйте з gcc -c evenness-main.c -o evenness-main.o:
#include <stdio.h>

extern int f(int n);

int main (int argc, char **argv) {
    int i;

    int testcases[] = { 14, 20, 94208, 7, 0, -4 };

    for (i = 0; i < sizeof(testcases) / sizeof(testcases[0]); i++) {
        printf("%d, %d\n", testcases[i], f(testcases[i]));
    }

    return 0;
}

Тоді:

  • Посилання: gcc evenness-main.o evenness.o -o evenness
  • Виконати: ./evenness

@FarazMasroor попросив більше детальних відомостей про те, як отримана ця відповідь.

Я більше знайомий з ніж тонкощі складання x86, тому, як правило, я використовую компілятор, щоб генерувати для мене збірний код. Я з досвіду знаю, що розширення gcc, такі як __builtin_ffs(), __builtin_ctz()і__builtin_popcount() зазвичай компілюють і збирають до 1 або 2 інструкцій на x86. Тому я почав з функції зразок:

int f(int n) {
    return __builtin_ctz(n);
}

Замість використання звичайної компіляції gcc аж до об'єктного коду, ви можете використовувати -Sопцію для компіляції просто до складання - gcc -S -c evenness.c. Це дає такий збірний файл evenness.s:

    .file   "evenness.c"
    .text
    .globl  f
    .type   f, @function
f:
.LFB0:
    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    movl    %edi, -4(%rbp)
    movl    -4(%rbp), %eax
    rep bsfl    %eax, %eax
    popq    %rbp
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE0:
    .size   f, .-f
    .ident  "GCC: (Ubuntu 4.8.4-2ubuntu1~14.04.1) 4.8.4"
    .section    .note.GNU-stack,"",@progbits

Багато цього можна виграти в гольф. Зокрема, ми знаємо, що режим виклику для функцій з підписом є приємним і простим - параметр введення передається в регістр, а повернене значення повертається в регістр. Тому ми можемо взяти більшість інструкцій - багато з них стосуються збереження регістрів та налаштування нового кадру стека. Ми не використовуємо стек тут і використовуємо лише регістр, тому не потрібно турбуватися про інші регістри. При цьому залишається код "збірки" для гольфу:int f(int n);EDIEAXEAX

    .globl  f
f:
    bsfl    %edi, %eax
    ret

Зауважте, як зазначає @zwol, ви також можете використовувати оптимізовану компіляцію для досягнення подібного результату. Зокрема, -Osвиробляються саме зазначені вище інструкції (з кількома додатковими директивами асемблера, які не створюють зайвого об'єктного коду.)

Тепер це зібрано, з gcc -c evenness.s -o evenness.oяким потім можна зв’язати програму тестового драйвера, як описано вище.

Існує кілька способів визначення машинного коду, відповідного цій збірці. Моя улюблена - використовувати команду gdb disassдемонтаж:

$ gdb ./evenness
GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.2) 7.7.1
...
Reading symbols from ./evenness...(no debugging symbols found)...done.
(gdb) disass /r f
Dump of assembler code for function f:
   0x00000000004005ae <+0>: 0f bc c7    bsf    %edi,%eax
   0x00000000004005b1 <+3>: c3  retq   
   0x00000000004005b2 <+4>: 66 2e 0f 1f 84 00 00 00 00 00   nopw   %cs:0x0(%rax,%rax,1)
   0x00000000004005bc <+14>:    0f 1f 40 00 nopl   0x0(%rax)
End of assembler dump.
(gdb) 

Тож ми можемо бачити, що машинний код для bsfінструкції є 0f bc c7і retє c3.


Чи не вважаємо це 2?
lirtosiast

2
Як дізнатись машинний / байтний дамп-код? Не можете знайти нічого в Інтернеті
Faraz Masroor

1
Це не відповідає умові виклику C. На x86-32 аргумент передається на стек; на x86-64 аргумент передається у% rdi. Це здається, що він працює лише у вашому тестовому ремені, тому що ваш компілятор залишив застарілу копію аргументу у% eax. Він зламається, якщо скласти джгути evenness-main.cз різними налаштуваннями оптимізації; для мене це ламає -O, -O2або -O3.
Anders Kaseorg

1
@AndersKaseorg - дякую, що вказали на це. Я обмежив це лише x86_64, так що цей вхід надходить у RDI.
Digital Trauma

3
"Крім того, незважаючи на документацію, яка говорить [...]" - Будь-яке отримане значення обов'язково узгоджується з документацією. Це не виключає, що інші моделі процесорів дають інше значення, ніж ваше.
hvd

25

Пітон, 25 байт

lambda n:len(bin(n&-n))-3

n & -n нулі нічого, крім найменш значущого біта, наприклад цього:

100010101010100000101010000
            v
000000000000000000000010000

Нас цікавить кількість кінцевих нулів, тому ми перетворюємо його у двійковий рядок, використовуючи bin, що для вищевказаного числа буде "0b10000". Оскільки нас не хвилює 0bані те 1, ані те , ми віднімаємо 3 від цієї довжини рядків.


після публікації своєї відповіді я зрозумів, що ваша дуже розумна, тому я спробував перетворити її на Pyth і побачити, чи ваш коротший, ніж мій. Він отримав l. & Q_Q, використовуючи log2 замість len (bin (_)). Це була така ж довжина, як моя відповідь Pyth, а також інша відповідь Pyth, схоже, це не стає коротше, ніж 6 байтів у Pyth ...
busukxuan


15

JavaScript (ES6), 18 байт

n=>Math.log2(n&-n)

4 байти коротше 31-Math.clz32. Ха-ха.


1
О, ух, і я нещодавно дізнався про це Math.clz32теж ...
Ніл

1
Чорт я збирався розмістити саме це! +1
Cyoce


6

Pyth, 8 байт

lec.BQ\1
     Q    autoinitialized to eval(input())
   .B     convert to binary string
  c   \1  split on "1", returning an array of runs of 0s
 e        get the last run of 0s, or empty string if number ends with 1
l         take the length

Наприклад, двійкове представлення 94208:

10111000000000000

Після розбиття на 1s та взяття останнього елемента результуючого масиву це:

000000000000

Це 12 нулів, так що це "12-річна рівність".

Це працює тому x / 2, що по суті x >> 1- це бітове змінення права 1. Тому число ділиться на 2 лише тоді, коли LSB є 0(як і десяткове число ділиться на 10, коли його остання цифра 0).



6

Pyth, 6 байт

x_.BQ1

В основному просто

convert2BinString(evaluatedInput())[::-1].index("1")

6

MATL , 5 байт

Yf2=s

Це працює для всіх цілих чисел.

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

Yf      % implicit input. Compute (repeated) prime factors. For negative input
        % it computes the prime factors of the absolute value, except that for
        % -1 it produces an empty array instead of a single 1
2=s     % count occurrences of "2" in the array of prime factors

"А тепер для чогось зовсім іншого ..."
стакан

6

C, 36 (28) байт

int f(int n){return n&1?0:f(n/2)+1;}

(Не було тестування на нульовий аргумент як ненульовий аргумент.)

Оновлення (у відповідь на коментар) : Якщо ми дозволяємо декларації функцій стилю K&R, ми можемо мати 28-байтну версію:

f(n){return n&1?0:f(n/2)+1;}

У цьому випадку ми покладаємося на те, що компілятор за замовчуванням використовує nі тип, і тип повернення fдо int. Ця форма генерує попередження за допомогою C99, але не компілює як дійсний код C ++.


Якщо ви зміните int n-> nце все-таки дійсний код C і відрізає 4 символи.
Джош

Влучне зауваження. Я збирався сказати, що це спрацьовує принаймні попередження щодо C99, але це так само опускає тип повернення. І обидві запускають помилки в C ++. Тож я відповідним чином змінюю свою відповідь.
Віктор Тот

5

Java 7, 39 або, може, 44 байти

int s(int a){return a%2!=0?0:s(a/2)+1;}

int s(int a){return a%2!=0|a==0?0:s(a/2)+1;}

Так, рекурсія! Мені довелося використовувати !=замість більш короткого порівняння, щоб воно не переливалося на негативний вхід, але крім того, що це досить просто. Якщо це дивно, надішліть нуль. Якщо навіть, додайте його та зробіть це ще раз.

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


4

JavaScript (ES6), 20 байт 19 байт.

f=x=>~x%2&&1+f(x/2)

Це порт рішення Haskell від @nimi до JavaScript. Він використовує властивості "короткого замикання", &&які повертають його ліву сторону, якщо вона є фальсією (що в даному випадку є -0) або ж повертає праву сторону. Тому для реалізації odd x = 0ми робимо ліву частину, 1 - (x % 2)яка пускається 0через &&, інакше ми повторимо 1 + f(x / 2).

Гоління 1 - (x % 2)як (~x) % 2обумовлене @Neil нижче, і має дивну властивість, яка змушує вищевказану функцію випромінювати -0для невеликих непарних чисел. Це значення є особливістю рішення JS про те, що цілі числа є подвійними IEEE754; ця система має окрему +0і -0які є спеціальними обсадженим в JavaScript , щоб бути ===один з одним. ~Оператор обчислює 32-бітну-знаково-ціле побітовое інверсії для числа, що для малих непарних чисел буде негативним парним числом. (Позитивне число, Math.pow(2, 31) + 1наприклад, виробляє, 0а не -0.) Дивне обмеження 32-бітових цілих чисел не має інших ефектів; зокрема це не впливає на правильність.


~x&1- байт коротший, ніж 1-x%2.
Ніл

@Neil Дуже круто. Це має дещо контр-інтуїтивну властивість, але я все одно візьмусь на це.
CR Drost

4

Perl 6, 23 18 байт

{+($_,*/2...^*%2)}

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

> my &f = {+($_,*/2...^*%2)}
-> ;; $_? is raw { #`(Block|117104200) ... }
> f(14)
1
> f(20)
2
> f(94208)
12
> f(7)
0
> f(-4)
2

4

Рубій 24 байти

Моє перше подання коду для гольфу (так!)

("%b"%$*[0])[/0*$/].size

Як я потрапив сюди :

Спершу я хотів отримати код, який насправді виконав специфікацію, щоб вирішити проблему, тому я побудував метод без огляду на кількість байтів:

def how_even(x, times=1)
  half = x / 2
  if half.even?
    how_even(half, times+1)
  else
    times
  end
end

За допомогою цих знань я повторно повторив функцію в циклі часу і додав $*(ARGV) як вхід, і я як підрахунок, у скільки разів вдвічі зменшилося число, перш ніж воно стає непарним.

x=$*[0];i=1;while(x=x/2)%2<1;i+=1;end;i

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

Тому я зібрав кілька результатів про те, як виглядали вхідні значення у двійковій формі:

input      in binary      result
---------------------------------
   14               1110   1
   20              10100   2
94208  10111000000000000  12

Я помітив, що результатом стало кількість позицій зліва, які ми маємо пройти, перш ніж число стане непарним.

Роблячи кілька простих маніпуляцій з рядком, я розділив рядок на останнє виникнення 1 і підрахував довжину решти 0:

("%b"%$*[0])[/0*$/].size

використовуючи ("%b" % x)форматування, щоб перетворити число на двійкове, а String # фрагмент - нарізати мій рядок.

На цьому квесті я дізнався кілька речей про рубіну і з нетерпінням чекаю на швидке появу більше гольфів!


2
Ласкаво просимо до головоломки програмування та обміну стека коду для гольфу. Це чудова відповідь; Мені дуже подобається пояснення. +1! Якщо ви хочете більше проблем з кодом-гольф, натисніть на тег код-гольф . Я з нетерпінням чекаю більше ваших відповідей.
wizzwizz4

1
Не соромтеся запитувати мене з будь-яких питань, які у вас є. Введіть @wizzwizz4на початку коментаря, щоб відповісти мені. (Це працює з усіма
іменами


4

C, 37 байт

f(int x){return x?x&1?0:1+f(x/2):0;} Рекурсивно перевіряйте останній біт, поки він не буде 0.


Крім того, є, f(int n){return __builtin_ctz(n);}якщо ви готові використовувати розширення gcc. Або навіть#define f __builtin_ctz
Digital Trauma

Видалити int . Це неявно, як і тип повернення.
luser droog

@luserdroog, ти маєш на увазі f(n){...}? GCC не збирає його. Я не фахівець з C, але швидкий пошук виявляє, що, можливо, ця функція була видалена в більш пізніх версіях C. Тож, можливо, вона буде компілюватися з відповідними прапорами?
Енді Соффер

@AndySoffer Я бачу. Може, -ansiчи -gnu99? Я знаю, що я змусив його працювати. Я написав поради щодо цього відповіді!
luser droog

3

Haskell, 28 байт

f x|odd x=0|1<2=1+f(div x 2)

Приклад використання: f 94208-> 12.

Якщо число непарне, результат є 0, інакше 1плюс рекурсивний дзвінок з половиною числа.


div x 2? Чому ні x/2?
CalculatorFeline

@CatsAreFluffy: Haskell має дуже сувору систему типу. divце ціле ділення, поділ з /плаваючою комою.
німі

3

Befunge, 20

&:2%#|_\1+\2/#
   @.<

Виконання коду продовжує рухатися вправо і обертатися до другого символу першого рядка (завдяки трейлінгу #) до 2%виходу 1, що призводить _до перемикання напрямку вліво, потім |вгору, яке обертається <на другий ряд, який виводить і виходить. Ми збільшуємо другий верхній елемент стеки кожен раз через петлю, потім ділимо верх на 2.


3

Сітківка ,29 17

+`\b(1+)\1$
;$1
;

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

2 байти збережено завдяки Мартіну!

Бере одинарне введення. Це неодноразово збігається з найбільшою кількістю 1s, вона може бути такою, що ця кількість 1s відповідає точно іншій частині 1s у кількості. Кожного разу, коли це робиться, він робить попередній ;перехід до рядка. В кінці підраховуємо кількість ;s в рядку.

Якщо ви хочете десятково вводити, додайте:

\d+
$0$*1

до початку програми.


3

Джольф, 6 байт

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

Zlm)j2
Zl   2  count the number occurrences of 2 in
  m)j   the prime factorization of j (input)

Досить просто ... Кудо на ETHProductions за витіснення Джольфа з версією, яка дійсно повинна працювати!


1
6 байтів, мабуть, є магічним числом цього виклику
Cyoce


3

6502 машинна мова, 7 байт

Щоб знайти місце значення найменш значущого 1 біта ненульового значення в акумуляторі, залишивши результат в регістрі X:

A2 FF E8 4A 90 FC 60

Щоб запустити це на тренажері 6502 на e-tradition.net , додайте до нього префікс з A9подальшим 8-бітним цілим числом.

Це розбирається на наступне:

count_trailing_zeroes:
    ldx #$FF
loop:
    inx
    lsr a     ; set carry to 0 iff A divisible by 2, then divide by 2 rounding down
    bcc loop  ; keep looping if A was divisible by 2
    rts       ; return with result in X

Це еквівалентно наступному C, за винятком того, що C intповинен бути принаймні 16-розрядним:

unsigned int count_trailing_zeroes(int signed_a) {
    unsigned int carry;
    unsigned int a = signed_a;  // cast to unsigned makes shift well-defined
    unsigned int x = UINT_MAX;
    do {
        x += 1;
        carry = a & 1;
        a >>= 1;
    } while (carry == 0);
    return x;
}

Те саме працює на 65816, припускаючи MX = 01 (16-розрядний акумулятор, 8-бітний індекс), і еквівалентно вищевказаному фрагменту C.


2

Брахілог , 27 15 байт

$pA:2xlL,Al-L=.

Пояснення

$pA             § Unify A with the list of prime factors of the input
   :2x          § Remove all occurences of 2 in A
      lL,       § L is the length of A minus all the 2s
         Al-L=. § Unify the output with the length of A minus L

2

CJam, 8 байт

rizmf2e=

Прочитайте ціле число, абсолютне значення, простий чинник, порахуйте два.


2

JavaScript ES6, 36 38 байт

Поле з двома байтами завдяки @ETHproductions

Відповідь досить нудна, але це робить свою роботу. Насправді це може бути занадто схоже на іншу відповідь, якщо він додасть запропоновані зміни, тоді я зніму свою.

b=>{for(c=0;b%2-1;c++)b/=2;alert(c)}

Для запуску призначте його змінній ( a=>{for...), оскільки це анонімна функція, а потім викликайте її a(100).


Гарна відповідь! b%2==0може бути змінено на b%2-1і c++може бути переміщено всередині останньої частини forзаяви. Я думаю, це також спрацює:b=>eval("for(c=0;b%2-1;b/=2)++c")
ETHproductions

@ETHproductions Так це може! Хороший улов :)
Коннор Белл

Ще один байт: b%2-1=> ~b&1Також я думаю, що це не вдається при введенні 0, що можна виправитиb&&~b&1
ETHproductions

Я заморозив комп'ютер, перевіряючи це на від’ємному числі. b%2-1перевірка невдач на негативні непарні числа.
Патрік Робертс

2

ES6, 22 байти

n=>31-Math.clz32(n&-n)

Повертає -1, якщо ви пройдете 0.


Ах, приємно. Я забув про clz32: P
Conor O'Brien

2

DUP , 20 байт

[$2/%0=[2/f;!1+.][0]?]f:

Try it here!

Перетворений на рекурсію, вихід зараз є найвищим числом у стеку. Використання:

94208[2/\0=[f;!1+][0]?]f:f;!

Пояснення

[                ]f: {save lambda to f}
 2/\0=               {top of stack /2, check if remainder is 0}
      [     ][ ]?    {conditional}
       f;!1+         {if so, then do f(top of stack)+1}
              0      {otherwise, push 0}

2

Japt, 9 5 байт

¢w b1

Перевірте це в Інтернеті!

Попередня версія мала бути п'ять байтів, але ця фактично працює.

Як це працює

       // Implicit: U = input integer
¢      // Take the binary representation of U.
w      // Reverse.
b1     // Find the first index of a "1" in this string.
       // Implicit output

2

C, 44 40 38 36 байт

2 байти від спасибі @JohnWHSmith . 2 байти від спасибі @luserdroog .

a;f(n){for(;~n&1;n/=2)a++;return a;}

Тестуйте наживо на ideone .


Можливо, ви зможете зняти 1 байт, замінивши дорогий !(n%2)на хороший ~n&1.
Джон У. Сміт

@JohnWHSmith. Це було приємно !! Подяка
знято

Видаліть =0. Глобали неявно ініціалізуються до 0.
luser droog

@luserdroog. Дякую, я про це не знав.
знято

Виправте мене, якщо я помиляюся, але оскільки ця функція використовує глобальну змінну a, чи не гарантовано це працювати лише вперше, коли вона викликана? Я не знав, що це дозволено.
Патрік Робертс
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.