Чи є ця цифра точною потужністю -2: (Дуже) жорсткий режим


26

Це версія недавнього виклику. Чи є це число цілою потужністю -2? з різним набором критеріїв, розроблених для висвітлення цікавого характеру проблеми та ускладнення виклику. Я поклав деякі міркування в нього тут .

Завдання, як це чудово заявив Тобі у зв’язаному питанні, полягає в наступному:

Існують розумні способи визначення, чи ціле число є точною силою 2. Це вже не є цікавою проблемою, тому давайте визначимо, чи задане ціле число є точною потужністю -2 . Наприклад:

-2 => yes: (-2)¹
-1 => no
0 => no
1 => yes: (-2)⁰
2 => no
3 => no
4 => yes: (-2)²

Правила:

  • Ціле число - 64 біт, підписане, два доповнення. Це єдиний тип даних, з яким ви можете працювати.
  • Ви можете використовувати лише наступні операції. Кожна з них вважається однією операцією.
    • n << k, n >> k: Зсув вліво / вправоn по kбітах. Біт знака подовжується в правий зсув.
    • n >>> k: Правий зсув, але не подовжуйте біт знака. 0 зміщено в.
    • a & b, a | b,a ^ b : Побітове І, АБО, що виключає АБО .
    • a + b, a - b,a * b : Додавання, віднімання, множення.
    • ~b: Побітове інвертування.
    • -b: Заперечення доповнення двох.
    • a / b, a % b : Розділити (цілий коефіцієнт, округлення до 0) та модуль.
      • У модулі від'ємних чисел використовуються правила, зазначені в С99 : (a/b) * b + a%bповинні дорівнювати a. Так 5 % -3є 2і -5 % 3є -2:
      • 5 / 3є 1, 5 % 3є 2, як 1 * 3 + 2 = 5.
      • -5 / 3є -1, -5 % 3є -2, як -1 * 3 + -2 = -5.
      • 5 / -3є -1, 5 % -3є 2, як -1 * -3 + 2 = 5.
      • -5 / -3 є 1 , -5 % -3є -2, як 1 * -3 + -2 = -5.
      • Зауважте, що //оператор ділення підлоги Python тут не задовольняє властивість поділу "круглого на 0", і %оператор Python також не відповідає цим вимогам.
    • Призначення не вважаються операцією. Як і в C, призначення оцінки до значення лівого боку після виконання завдання: a = (b = a + 5)набори bдля a + 5, потім встановлюєшa до b, і розраховує як одну операцію.
    • Складні призначення можуть використовуватися a += bзасобами a = a + bі рахуватися як одна операція.
  • Ви можете використовувати цілі константи, вони не вважаються нічим.
  • Дужки, що визначають порядок операцій, є прийнятними.
  • Ви можете оголосити функції. Декларації функцій можуть бути в будь-якому зручному для вас стилі, але зауважте, що 64-бітні цілі числа - єдиний дійсний тип даних. Оголошення функції не вважаються операціями, але виклик функції вважається одним. Крім того, щоб бути зрозумілим: Функції можуть містити кілька returnзаяв, і returns з будь-якої точки дозволено. Thereturn по собі не вважається операцією.
  • Ви можете декларувати змінні безкоштовно.
  • Ви можете використовувати whileпетлі, але ви не можете використовувати ifабо for. Оператори, які використовуються в whileумові, враховують ваш рахунок. whileциклі виконуються до тих пір, поки їх умова оцінюється до нульового значення ("truthy" 0 в мовах, які мають це поняття, не є дійсним результатом). Оскільки дозволено повернення дозволено, ви можете використовуватиbreak , а також
  • Переповнення / перетікання дозволено, і затискання значень не буде здійснюватися. Це трактується так, ніби операція насправді сталася правильно, а потім була обрізана до 64 біт.

Критерії оцінювання / виграшу:

Ваш код повинен створювати значення, яке не дорівнює нулю, якщо вхід має потужність -2, а нуль інакше.

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

function example (a, b) {
    return a + ~b;
}

function ispowerofnegtwo (input) {
    y = example(input, 9);
    y = example(y, 42);
    y = example(y, 98);
    return y;
}

Містить 5 операцій: дві у функції та три виклики функції.

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

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

Виклик бонус дуже важкий режим!

Для того, щоб виграти абсолютно нічого, крім потенційної здатності справити враження на людей на вечірках, подайте відповідь, не використовуючи whileциклів! Якщо їх подано достатньо, я навіть можу розглянути питання про поділ переможних груп на дві категорії (з циклами та без них).


Примітка. Якщо ви хочете надати рішення мовою, яка підтримує лише 32-бітні цілі числа, ви можете зробити це за умови, що ви достатньо обґрунтуєте, що це все ще буде правильним для 64-бітових цілих чисел у поясненні.

Також: Певні особливості мови можуть бути дозволені безкоштовно, якщо вони не ухиляються від правил, але необхідні для примушування вашої мови до поведінки відповідно до вищезазначених правил . Наприклад, (надумано), я дозволю вільне не рівне 0 порівнянню в whileциклі, коли його застосовують до умови в цілому, як вирішення для мови, що має "truthy" 0. Чіткі спроби скористатися тими речами не дозволяються - наприклад, поняття "truthy" 0 або "undefined" значень не існує у наведеному вище наборі правил, і тому на них не можна покластися.


Коментарі не для розширеного обговорення; ця розмова переміщена до чату .
Денніс

@hvd Якщо ви читаєте це: Ви повинні повністю відновити відповідь! Якщо припустити, що це правильно, навіть якщо m ^= s це все ще не вражає, і я думаю, було б цілком нормально зробити заміну, щоб покращити її ще більше.
Джейсон C

Як це має сенс , щоб дозволити whileі , breakале не if? if (x) { ... }еквівалентно while (x) { ... break; }.
R ..

@R .. Це не має 100% сенсу ( breakа раннє повернення - шкодуючий склад) і це довга історія та урок, виведений з правил для майбутніх викликів. Завжди є "бонусна" версія! :)
Джейсон C

1
Чому ifі forзаборонено? int x=condition; while (x) { ... x=0; }безкоштовно, просто більше коду. Те ж саме і з c-стилем for.
Qwertiy

Відповіді:


35

C ++, 15 операцій

Я поняття не маю, чому whileдозволені петлі, оскільки вони руйнують весь виклик. Ось відповідь без жодної:

int64_t is_negpow2(int64_t n) {
    int64_t neg = uint64_t(n) >> 63; // n >>> 63
    n = (n ^ -neg) + neg; // if (n < 0) n = -n;
    int64_t evenbits = n & int64_t(0xaaaaaaaaaaaaaaaaull >> neg);
    int64_t n1 = n - 1;
    int64_t pot = n & n1;
    int64_t r = pot | (n1 >> 63) | evenbits;
    return ~((r | -r) >> 63); // !r
}

Чому whileпетлі знищують весь виклик ?
Містер Xcoder

10
@ Mr.Xcoder Тому що завдання полягає в тому, щоб робити це з простими побітними операціями і whileвсіляко йде проти цього.
orlp

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

Я зробив коментар про це тут .
Джейсон C

@JasonC Це тому, що я повинен був використовувати правильний зсув без бітових знаків. Я відредагував код (він використовується, uint64_tтому що це єдиний спосіб отримати правильний зсув без розширення знаків.)
orlp

25

Python 2 , 3 операції

def f(n):
 while n>>1:
  while n&1:return 0
  n=n/-2
 return n

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

Ці операції >>, &, /.

Ідея полягає в тому, щоб повторно ділити на -2. Повноваження -2 ланцюга аж до 1: -8 -> 4 -> -2 -> 1. Якщо ми потрапили на 1, прийміть. Якщо ми потрапили на непарне число перед ударом 1, відхиліть. Нам також потрібно відкинути 0, що назавжди переходить до себе.

while n>>1:Петель до тих пір , nпоки 0 або 1. Коли цикл переривається, nсам по собі повертається, і 1є виходом Truthy і0 Falsey один. Всередині циклу ми неодноразово відхиляємо застосування n -> n/-2та відхиляємо будь-які непарні n.

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


Приємно. Розумна логіка в алгоритмі та хороша робота, поєднуючи умови в бітові операції. Також можна підтвердити, що впровадження працює в C.
Jason C

Чому while n&1замість if n&1?
Марк Рансом

2
@MarkRansom Виклик не дозволяє if.
xnor

Ага, пропустив це. Дуже розумна підміна.
Марк Рансом

1
@EvSunWoodard Оцінка балів - це кількість операторів у коді, а не кількість дзвінків до них під час виконання, що залежить від введення: "Це атомний код-гольф. Ваш рахунок - це загальна кількість операцій, присутніх у вашому коді . "
xnor

11

Іржа, 14 12 операцій (без циклів)

Потрібна оптимізація ( -O) або -C overflow-checks=noввімкнення переповнення віднімання замість паніки.

fn is_power_of_negative_2(input: i64) -> i64 {
    let sign = input >> 63;
    // 1 op
    let abs_input = (input ^ sign) - sign;
    // 2 ops
    let bad_power_of_two = sign ^ -0x5555_5555_5555_5556; // == 0xaaaa_aaaa_aaaa_aaaa
    // 1 op
    let is_not_power_of_n2 = abs_input & ((abs_input - 1) | bad_power_of_two);
    // 3 ops 
    let is_not_power_of_n2 = (is_not_power_of_n2 | -is_not_power_of_n2) >> 63;
    // 3 ops 
    input & !is_not_power_of_n2
    // 2 ops
}

(Для уточнення: !xце побітовое НЕ тут, а не логікою-НЕ)

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

#[test]
fn test_is_power_of_negative_2() {
    let mut value = 1;
    for _ in 0 .. 64 {
        assert_ne!(0, is_power_of_negative_2(value), "wrong: {} should return nonzero", value);
        value *= -2;
    }
}

#[test]
fn test_not_power_of_negative_2() {
    for i in &[0, -1, 2, 3, -3, -4, 5, -5, 6, -6, 7, -7, 8, 1<<61, -1<<62, 2554790084739629493, -4676986601000636537] {
        assert_eq!(0, is_power_of_negative_2(*i), "wrong: {} should return zero", i);
    }
}

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


Ідея полягає у тому, щоб перевірити, чи | x | є потужністю 2 (використовуючи (y & (y - 1)) == 0як завжди). Якщо x - потужність 2, то ми додатково перевіряємо (1) коли x >= 0, вона також повинна бути парною силою 2, або (2) коли x < 0, це має бути непарна потужність 2. Ми перевіряємо це, &відмінюючи " bad_power_of_two"маска 0x ... aaaa when x >= 0(видає 0 лише тоді, коли вона є рівною силою), або 0x ... 5555, коли x < 0.


Я вкрав твою ~((r | -r) >> 63)хитрість, щоб закінчити виправити свою відповідь.
orlp

6

Хаскелл, 2 3 операції

import Data.Bits (.&.)

f 0 = False
f 1 = True
f n | n .&. 1 == 0 = f (n `div` -2)
f n | otherwise    = False

Визначає рекурсивну функцію f(n). Використовувані операції - це виклик функції ( f), поділ ( div), порозрядне і ( .&.).

Не містить циклів через те, що у Haskell немає циклів :-)


4
Чому я не здивований, що рішення Haskell з використанням циклів не надає хтось на ім'я "Opportunist?" =)
Корт Аммон - Відновіть Моніку

1
Я дуже вагається про f 0, f 1, f n ...тут , тому що вони, по суті , if«S в маскуванні, але знову ж , я дозволити while+ breakі рано returns, так що здається справедливим. Хоча це, здається, скористається тим, що мій набір правил ненавмисно залишається відкритим для тлумачення, це приємне рішення.
Джейсон C

3
Зокрема |, це особливо в повітрі. Однак, це порушує одне конкретне правило менш дискусійним чином: порівняння ==не допускається. Однак слід зазначити, що якщо моя інтерпретація цього коду є правильною, використання булевих тут же з'являється прийнятним, підставляючи довільні цілі значення на їхньому місці не з'являється , щоб змінити результати, і вони більше кінцевої форми подання.
Джейсон С

@JasonC я використовую лише ==тому, що в Haskell немає іншого способу відійти від "Truthy" Intдо Bool"Truthy". Порушують чи відповідність шаблону та захисні ifправила правилу "без s" - ваш дзвінок ;-)
Opportunist

18
Завдяки відповідності шаблонів ви можете просто жорстко кодувати результати для всіх 64-бітних цілих чисел, використовуючи 0 операцій.
xnor

5

Python 3, 10 або 11 9 операцій

def g(x):
 while x:
  while 1 - (1 + ~int(x - -2 * int(float(x) / -2))) & 1: x /= -2
  break
 while int(1-x):
     return 0
 return 5  # or any other value

Повертає 5для повноважень -2, в 0іншому випадку


Коментарі не для розширеного обговорення; ця розмова переміщена до чату .
Денніс

5

C, 5 операцій

long long f(long long x){
    x=x ^ ((x & 0xaaaaaaaaaaaaaaaa) * 6);
    while(x){
        while(x&(x-1))
            return 0;
        return 1;
    }
    return 0;
}

C, 10 операцій, без петель

long long f(long long x){
    x = x ^ ((x & 0xaaaaaaaaaaaaaaaa) * 6);
    long long t = x & (x-1);
    return (((t-1) & ~t) >> 63) * x;
}

C, 1 операція

long long f(long long x){
    long long a0=1, a1=-2, a2=4, a3=-8, a4=16, a5=-32, a6=64, a7=-128, a8=256, a9=-512, a10=1024, a11=-2048, a12=4096, a13=-8192, a14=16384, a15=-32768, a16=65536, a17=-131072, a18=262144, a19=-524288, a20=1048576, a21=-2097152, a22=4194304, a23=-8388608, a24=16777216, a25=-33554432, a26=67108864, a27=-134217728, a28=268435456, a29=-536870912, a30=1073741824, a31=-2147483648, a32=4294967296, a33=-8589934592, a34=17179869184, a35=-34359738368, a36=68719476736, a37=-137438953472, a38=274877906944, a39=-549755813888, a40=1099511627776, a41=-2199023255552, a42=4398046511104, a43=-8796093022208, a44=17592186044416, a45=-35184372088832, a46=70368744177664, a47=-140737488355328, a48=281474976710656, a49=-562949953421312, a50=1125899906842624, a51=-2251799813685248, a52=4503599627370496, a53=-9007199254740992, a54=18014398509481984, a55=-36028797018963968, a56=72057594037927936, a57=-144115188075855872, a58=288230376151711744, a59=-576460752303423488, a60=1152921504606846976, a61=-2305843009213693952, a62=4611686018427387904, a63=-9223372036854775807-1, a64=0;
    while(a0){
        long long t = x ^ a0;
        long long f = 1;
        while(t){
            f = 0;
            t = 0;
        }
        while(f)
            return 1;
        a0=a1; a1=a2; a2=a3; a3=a4; a4=a5; a5=a6; a6=a7; a7=a8; a8=a9; a9=a10; a10=a11; a11=a12; a12=a13; a13=a14; a14=a15; a15=a16; a16=a17; a17=a18; a18=a19; a19=a20; a20=a21; a21=a22; a22=a23; a23=a24; a24=a25; a25=a26; a26=a27; a27=a28; a28=a29; a29=a30; a30=a31; a31=a32; a32=a33; a33=a34; a34=a35; a35=a36; a36=a37; a37=a38; a38=a39; a39=a40; a40=a41; a41=a42; a42=a43; a43=a44; a44=a45; a45=a46; a46=a47; a47=a48; a48=a49; a49=a50; a50=a51; a51=a52; a52=a53; a53=a54; a54=a55; a55=a56; a56=a57; a57=a58; a58=a59; a59=a60; a60=a61; a61=a62; a62=a63; a63=a64;
    }
    return 0;
}

2
О людино, останній - це просто зло. Приємно.
Джейсон C

4

Збірка, 1 операція

.data

    .space 1         , 1 # (-2)^31
    .space 1610612735, 0
    .space 1         , 1 # (-2)^29
    .space 402653183 , 0
    .space 1         , 1 # (-2)^27
    .space 100663295 , 0
    .space 1         , 1 # (-2)^25
    .space 25165823  , 0
    .space 1         , 1 # (-2)^23
    .space 6291455   , 0
    .space 1         , 1 # (-2)^21
    .space 1572863   , 0
    .space 1         , 1 # (-2)^19
    .space 393215    , 0
    .space 1         , 1 # (-2)^17
    .space 98303     , 0
    .space 1         , 1 # (-2)^15
    .space 24575     , 0
    .space 1         , 1 # (-2)^13
    .space 6143      , 0
    .space 1         , 1 # (-2)^11
    .space 1535      , 0
    .space 1         , 1 # (-2)^9
    .space 383       , 0
    .space 1         , 1 # (-2)^7
    .space 95        , 0
    .space 1         , 1 # (-2)^5 = -32
    .space 23        , 0
    .space 1         , 1 # (-2)^3 = -8
    .space 5         , 0
    .space 1         , 1 # (-2)^1 = -2
    .space 1         , 0
dataZero:
    .space 1         , 0
    .space 1         , 1 # (-2)^0 = 1
    .space 2         , 0
    .space 1         , 1 # (-2)^2 = 4
    .space 11        , 0
    .space 1         , 1 # (-2)^4 = 16
    .space 47        , 0
    .space 1         , 1 # (-2)^6 = 64
    .space 191       , 0
    .space 1         , 1 # (-2)^8
    .space 767       , 0
    .space 1         , 1 # (-2)^10
    .space 3071      , 0
    .space 1         , 1 # (-2)^12
    .space 12287     , 0
    .space 1         , 1 # (-2)^14
    .space 49151     , 0
    .space 1         , 1 # (-2)^16
    .space 196607    , 0
    .space 1         , 1 # (-2)^18
    .space 786431    , 0
    .space 1         , 1 # (-2)^20
    .space 3145727   , 0
    .space 1         , 1 # (-2)^22
    .space 12582911  , 0
    .space 1         , 1 # (-2)^24
    .space 50331647  , 0
    .space 1         , 1 # (-2)^26
    .space 201326591 , 0
    .space 1         , 1 # (-2)^28
    .space 805306367 , 0
    .space 1         , 1 # (-2)^30
    .space 3221225471, 0
    .space 1         , 1 # (-2)^32

.globl isPowNeg2
isPowNeg2:
    movl dataZero(%edi), %eax
    ret

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


1
Індексація таблиці не є однією з дозволених операцій.
R ..

1
Крім того, це явно не можна розширити до 64 біт. :-)
R ..

Дійсно, індексування таблиці не передбачалося дозволено згідно з чинними правилами. Я вказав "ви можете оголосити змінні" і "ви можете вказати цілі літерали" з наміром скалярів, і семантично це масив (і педантично кажучи, я не дозволяв типів масивів, і не дозволяв індексувати будь-який вид як один з операції, хоча ви могли назвати це "доповненням" у контексті асемблера), але будучи Опортуністом, що ви ... :)
Джейсон C

3

C, 31 операція

Демо демо

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

int isPositive(int x) // 6
{
    return ((~x & (~x + 1)) >> 31) & 1;
}

int isPowerOfTwo(int x) // 5
{
    return isPositive(x) & ~(x & (x-1));
}

int log2(int x) // 3
{
    int i = (-1);

    while(isPositive(x))
    {
        i  += 1;
        x >>= 1;
    }

    return i;
}

int isPowerOfNegativeTwo(int x) // 17
{
    return (  isPositive(x) &  isPowerOfTwo(x) & ~(log2(x) % 2) )
         | ( ~isPositive(x) & isPowerOfTwo(-x) & (log2(-x) % 2) );
}

1
Ви насправді зробили краще, ніж ви думаєте. Виклик функції вважається лише 1, а не кількість операторів функції. Отже, якщо я правильно порахував (подвійна перевірка), у вас є щось на зразок 6 для isPositive + 5 для isPowerOfTwo + 3 для log2 + 17 для isPowerOfNegativeTwo = 31.
Jason C

1

C, 7 операцій

int64_t is_power_of_neg2(int64_t n)
{
    int64_t x = n&-n;
    while (x^n) {
        while (x^-n)
            return 0;
        return x & 0xaaaaaaaaaaaaaaaa;
    }
    return x & 0x5555555555555555;
}

або:

C, 13 операцій без циклів як умовних

int64_t is_power_of_neg2(int64_t n)
{
    int64_t s = ~(n>>63);
    int64_t a = ((n/2)^s)-s;
    int64_t x = n&-(uint64_t)n; // Cast to define - on INT64_MIN.
    return ~(a/x >> 63) & x & (0xaaaaaaaaaaaaaaaa^s);
}

Пояснення:

  • n&-nдає найменший встановлений біт n.
  • a- це заперечне абсолютне значення n/2, обов'язково негативне, оскільки /2виключає переповнення заперечення.
  • a/xдорівнює нулю, лише якщо aточна потужність два; в іншому випадку встановлюється принаймні один інший біт, і він вищий за xнайнижчий біт, даючи негативний результат.
  • ~(a/x >> 63)то дає біт-маску, яка є все-єдиною, якщо nабо -nє силою двох, інакше всі нулі.
  • ^sзастосовується до маски, щоб перевірити, nчи є це сила -2.

1

PHP, 3 операції

потрійні і ifзаборонені; тож давайте зловживаємо while:

function f($n)
{
    while ($n>>1)               # 1. ">>1"
    {
        while ($n&1)            # 2. "&1"
            return 0;
        return f($n/-2|0);      # 3. "/-2" ("|0" to turn it into integer division)
    }
    return $n;
}
  1. $n>>1: якщо число 0 або 1, поверніть номер
  2. $n&1: якщо число непарне, поверніть 0
  3. тест else $n/-2(+ кинути в int)

0

JavaScript ES6, 7 операцій

x=>{
  while(x&1^1&x/x){
    x/=-2;x=x|0
  }
  while(x&0xfffffffe)x-=x
  return x
}

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

Пояснення

while(x&1^1&x/x)

Тоді як x! = 0 і x% 2 == 0 4 ops
x / x дорівнює 1, доки x не дорівнює 0 (0/0 дає NaN, який оцінюється як хибний)
& порозрядно, а
x & 1 ^ 1 дорівнює 1 якщо x є парним (x і 1) xor 1

x/=-2;x=x|0

Це форма поділу, визначена питанням 1 оп

while(x&0xfffffffe)  

Тоді як x! = 1 і x! = 0 1 op
Умова, необхідна для виходу, коли x == 0 або x == 1, оскільки ці два є поверненими значеннями, і введення нескінченного циклу не було б результативним. Теоретично це можна збільшити для отримання більших значень за рахунок збільшення шістнадцяткової кількості. В даний час працює до ± 2 ^ 32-1

x-=x

Встановіть х на 0 1 оп
Хоча я міг би використовувати return 0 за 0 ops, я відчував, що будь-який цикл while, який розірваний іншим твердженням, дуже схожий на обман.

return x

повертає х (1, якщо потужність -2, 0 в іншому випадку)

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