Цифрова твердість цілих чисел


26

Щоб знайти цифрову твердість цілого числа, візьміть його двійкове представлення і підрахуйте кількість разів, яку можна вилучити як провідним, так і кінцевим, 1поки воно не починається або закінчується знаком a 0. Загальна кількість вилучених біт - це його цифрова твердість.

Це досить багатослівне пояснення - тож давайте розбимо його на відпрацьованому прикладі.

Для цього прикладу ми будемо використовувати число 3167. У двійковому вигляді це:

110001011111

(Зверніть увагу, що під час перетворення у двійкові ви повинні переконатися у позбавленні провідних нулів)

Це не починається і не закінчується 0, тому ми видаляємо 1 пару біт:

1  1000101111  1

І ще:

11  00010111  11

Але зараз на початку є 0, тому ми не можемо видалити більше 1пар. Загалом 4 біта ми видалили, і так 4 - цифрова твердість 3167.

Однак для чисел, які можна записати як 2 n -1 для додатного n (тобто містять лише 1у двійковому поданні), 0 ніколи не буде досягнуто, і тому всі біти можна буде видалити. Це означає, що твердість - це просто ціла довжина біта.


Змагання

Ваше завдання полягає в тому, щоб написати програму або функцію, яка з урахуванням невід'ємного цілого числа n >= 0визначає її цифрову твердість.

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


Випробування

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

0     -> 0
1     -> 1
8     -> 0
23    -> 2
31    -> 5
103   -> 4
127   -> 7
1877  -> 2
2015  -> 10

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

def hardness(num) -> int:
    binary = bin(num)[2:]

    if binary.count('0') == 0:
        return num.bit_length()

    revbin = binary[::-1]

    return min(revbin.find('0'), binary.find('0')) * 2

1
Як 1повернути 1, коли в ньому немає жодного 0? Я маю на увазі, ви не можете видалити достатню кількість знаків із рядка, щоб почати чи закінчити її 0.
busukxuan

2
@busukxuan Прочитайте абзац перед заголовком "Виклик": для чисел, які можна записати як 2 ^ n-1 (тобто містять лише 1 у двійковому зображенні), 0 ніколи не буде досягнуто, і тому всі біти можна буде видалити . Це означає, що твердість - це просто ціла довжина біта.
FlipTack

2
@busukxuan ви можете подумати про це як про кількість одиниць, з яких сторона забита, до досягнення нулів.
FlipTack

2
Тим, хто не любив крайових випадків: твердість - це кількість твердих (1) біт, якими вона забита - якщо вся тверда речовина, то, безумовно, вона має 100% твердість, всю її довжину біта?
FlipTack

1
@FlipTack Я не хочу занадто сильно впливати, це ваше завдання. Я спочатку розумів "твердість" як максимальну кількість пар зовнішніх, які можна зняти, по одній з кожного боку. Але ви можете мати рацію, якщо в кінці залишиться жодна, можливо, це слід порахувати
Луїс Мендо

Відповіді:


6

Желе , 11 10 байт

BµQL××Ṛa\S

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

Як це працює

BµQL××Ṛa\S  Main link. Argument: n

B           Binary; convert n to base 2.
 µ          Begin a new, monadic chain. Argument: A (array of binary digits)
  Q         Unique; deduplicate the digits.
   L        Length; count the unique digits.
    ×       Multiply each digit by the result.
     ×Ṛ     Multiply the results by reversed A.
       a\   Cumulative reduce by logical AND.
            This zeroes out all elements after the first zero.
         S  Compute the sum of the result.

8

Пітон , 76 69 68 63 62 60 57 байт

f=lambda n,k=0:n>>k&(n&n>>k>n>>k+1)and(n&n+1>0)-~f(n,k+1)

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

Як це працює

Це рекурсивне рішення, яке приймає вхід n і продовжує збільшувати k - починаючи з 0 - в той час як LSB k (n) (біт в індексі k справа) і MSB k (n) (біт в індексі k зліва) встановлюються. Закінчивши, він повертає k, якщо встановлено весь біт n , а 2k - якщо ні.

Почнемо з переписування лямбда f як названої функції F з допоміжною змінною t .

def F(n, k = 0):
    t = n >> k
    return t & (n & t > t >> 1) and (n & (n + 1) > 0) + 1 + F(n, k + 1)

У кожному виклику F спочатку ми зсуваємо n загалом k одиниць праворуч і зберігаємо результат у t . Таким чином, LSB 0 (t) = LSB k (n) , тому t непарне, якщо і тільки якщо встановлено LSB k (n) .

Визначення того, чи встановлено MSB k (n) , є дещо складнішим; це те, чого n & t > t >> 1досягається. Щоб проілюструвати, як воно працює, розглянемо ціле число n = 1αβγδεζη 2 бітової довжини 8 та проаналізуємо виклик функції F (n, 3) , тобто k = 3 .

Ми намагаємося визначити, чи встановлено MSB 3 (n) = γ , досліджуючи значення істинності порівняння (n & t> t >> 1) = (1αβγδεζη 2 & 1αβγδ 2 > 1αβγ 2 ) . Розглянемо залучені цілі числа.

MSB-index  012k4567

n          1αβγδεζη
t             1αβγδ

t >> 1         1αβγ

Ми стверджуємо, що γ = 1 тоді і тільки тоді, коли n & t> t >> 1 .

  • Якщо γ = 1 , то n & t має бітову довжину 5, тоді як t >> 1 має бітну довжину 4 , тому n & t> t >> 1 .

    Це доводить, що γ = 1 означає n & t> t >> 1 .

  • Якщо n & t> t >> 1 , є два варіанти: або γ = 1, або γ = 0 . У першому випадку доводиться нічого не доводити.

    У другому випадку маємо, що αβγδ 2 ≥ n & t> t >> 1 = 1αβγ 2 .

    Оскільки αβγδ 2 > 1αβγ 2 , у нас повинен бути MSB 0 (αβγδ 2 ) ≥ MSB 0 (1αβγ 2 ) , тобто α = 1 .

    Таким чином, 1βγδ 2 > 11βγ 2 , тому ми повинні мати MSB 1 (1βγδ 2 ) ≥ MSB 1 (11βγ 2 ) , що означає, що β = 1 .

    У свою чергу це означає, що 11γδ 2 > 111γ 2 . Пам'ятаючи, що γ = 0 у другому випадку, ми отримуємо нерівність 110δ 2 > 1110 2 , яка помилкова, оскільки MSB 2 (110δ 2 ) = 0 <1 = MSB 2 (1110 2 ) .

    Таким чином, можливий лише перший випадок, і n & t> t >> 1 означає γ = 1 .

Підводячи підсумок, якщо обидва LSB до (п) і MSB до (п) встановлені, т буде непарних і п &> т >> 1 буде Правда , так т & (п &> т >> 1) буде вихід 1 . Однак якщо LSB k (n) або MSB k (n) не встановлено (або якщо обидва є), t буде рівним або n & t> t >> 1 буде помилковим , тому t & (n & t> t> > 1) дасть 0 .

Виклик F одним аргументом ініціалізує k = 0 . Хоча умова, про яку ми говорили раніше, виконується, після andцього виконується код , який (серед іншого) рекурсивно викликає F із збільшенням k .

Як тільки LSB k (n) або MSB k (n) не встановлено, умова провалюється, і F (n, k) повертає 0 . Кожен з попередніх k викликів функції додає (n & (n + 1)> 0) + 1 до F (n, k) = 0 , тому F (n) повертається ((n & (n + 1)> 0) + 1) к .

Тепер, якщо всі біти n рівні (тобто, якщо n дорівнює 0 або всі його біти встановлені), n + 1 не матиме жодного спільного біта з n , тому n & (n + 1) = 0 і F (n) повертає k . Однак якщо n має як встановлені, так і невідомі біти, n & (n + 1)> 0 і F (n) повертає 2k .


2
Рекурсивні рішення в Python, здається, забивають дуже добре останнім часом.
mbomb007

Принаймні порівняно з ітераційними рішеннями вони завжди є. input(), whileІ printвже 17 байт ...
Денніс

Так, але мені їх важче написати.
mbomb007

1
Досить справедливо. Пряма ітеративна реалізація тієї самої ідеї була б лише на 5 байт довше. tio.run/nexus/… ще 2 байти можна зберегти за допомогою декількох прийомів. tio.run/nexus/python2#JY1BDsIgFAXX7SnepgUUI1BNm1K4jKVJQ/…
Dennis

6

MATL , 13 12 байт

Btv`6L&)}x@q

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

Пояснення

Код повторює кожну двійкову цифру і підраховує, скільки разів можна видалити дві зовнішні.

B        % Input number (implicit). Horizontal vector of binary digits
tv       % Duplicate and concatenate vertically
`        % Do...while
  6L&)   %   Flatten the array if needed (in column-major order), and split it
         %   into two subarrays: one with the inner entries, and another
         %   with the two outer entries. The latter will be used for deciding
         %   if the loop continues or is exited
}        % Finally (execute before exiting the loop)
  x      %   Delete last subarray of inner entries
  @q     %   Push last iteration index minus 1
         % End (implicit). The next iterarion is executed if the array at the
         % top of the stack is non-empty and only contains nonzero values. 
         % Otherwise the loop is exited, executing the "finally" block first
         % Display (implicit)

6

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

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

def f(n):b=bin(n)[2:];x=min(b.find('0'),b[::-1].find('0'));print(x<0)*len(b)or x*2

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

Хоча це працює аналогічно програмі Python в ОП, я створив це до того, як питання було розміщене, після перегляду питання в пісочниці, яка не містила такої програми.


6

Python 2, 66 байт

s=bin(input())[2:].split('0')
print len(min(s[-1],s[0]))<<1%len(s)

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


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

5
@ mbomb007 Прийміть це як розминку для розуміння Денніса :)
xnor

3

PowerShell , 109 106 байт

$a=[convert]::ToString($args[0],2)-split0;(((($b=$a[0].length),$a[-1].length|sort)[0]*2),$b)[$a.count-eq1]

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

Здійснює вхід $args[0], використовує виклик .NET для convertцього toStringз базою 2(тобто робить його двійковим), потім -splits ця рядок 0s, зберігає це в $a. Важливо зауважити: виклик .NET не повертає провідні нулі, тому перша цифра завжди є a 1.

Таким чином, є дві можливості - двійковий рядок - всі ці, або був принаймні один нуль. Ми розрізняємо ті, у кого псевдотермінал індексовано $a.count-eq1. Якщо двійковий файл має принаймні один нуль, лівий випадок, ми беремо мінімум довжини першого [0]рядка 1s та останнього [-1]рядка (знайденого до |sortта тоді [0]). Чим коротший з них - це найбільше пар, які ми могли б видалити, тому множимо це на 2. Зауважте, що якщо початковий двійковий рядок закінчується на a 0, як для введення 8, то [-1].lengthтакож буде 0(оскільки це порожній рядок), який при множенні на 2досі є 0.

В іншому випадку з бінарним рядком всі вони беремо просто $b(що раніше було встановлено як довжина першого [0]рядка, в даному випадку - цілість двійкового рядка).

В будь-якій ситуації цей результат залишається на конвеєрі, а вихід неявний.


3

JavaScript (ES6), 57 байт

f=
n=>n.toString(2).replace(/^(1*)(.*(\1))?$/,'$1$3').length
<input oninput=o.value=1/this.value?f(+this.value):''><input id=o readonly>

Бере бінарне і намагається співставити все 1sабо не відповідає рівній кількості провідних і кінцевих 1s.


2

Сітківка , 48 байт

.+
$*
+`(1+)\1
$1o
o1
1
m(+`^1(.*)1$
xx¶$1
x|^1$

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

Пояснення:

.+              # Convert to unary
$*
+`(1+)\1        # Convert to binary (but with `o` instead of `0` -- it's shorter)
$1o
o1
1
m(+`^1(.*)1$    # Replace pairs of surrounding ones with `xx`
xx¶$1
x|^1$,          # Count x's, including the possibility of a single remaining `1`

2

C #, 133 байт

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

int h(int b){var n=Convert.ToString(b,2);for(b=0;;){if(n[0]+n[n.Length-1]==98)n=n.Substring(1,n.Length-2);else break;b+=2;}return b;}

Що ж, сьогодні я дізнався '1' + '1' = 98в C #.


1
Це тому, що '1'це
знак

Я буквально витратив 10 хвилин, з'ясовуючи, чому моє 1 + 1 = 2не працює. @FlipTack
devRicher

2

C, 89 88 85 байт

Збережено два байти завдяки @FlipTack, що вказує на марну декларацію.

Подзвоніть f()з номером для тестування, вихід повертається з функції.

t,h;f(l){for(t=l;t&&~t&1<<30;t*=2);for(h=0;t&1<<30&&l&1;t*=2,l/=2)++h;return h<<!!l;}

Спробуйте це на ideone .




2

С, 137 132 122 119 117 114 98 94 92 87 85 байт

Час почати займатися гольфом B-)

i,j;f(n){for(i=1<<30;i&~n;i/=2);for(j=0;n&i;n/=2,i/=4)j+=~n&1?i=0:2;return j-=n<1*j;}

Ось доказ

main()
{
  printf("%d %d\n", 0, f(0));
  printf("%d %d\n", 1, f(1));
  printf("%d %d\n", 8, f(8));
  printf("%d %d\n", 23, f(23));
  printf("%d %d\n", 31, f(31));
  printf("%d %d\n", 103, f(103));
  printf("%d %d\n", 127, f(127));
  printf("%d %d\n", 1877, f(1877));
  printf("%d %d\n", 2015, f(2015));
  printf("%d %d\n", 3167, f(3167));
} 

і вихід;

0 0
1 1
8 0
23 2
31 5
103 4
127 7
1877 2
2015 10
3167 4 


1

Математика, 63 56 байт

(2-Min[l=#~IntegerDigits~2])Min[Tr/@Split[l][[{1,-1}]]]&

Пояснення

l=#~IntegerDigits~2

Створіть представлення бази-2 на вході, обгорненому символом a List . Зберігайте це вl

(2-Min[...])

Якщо елемент min l становить 1, виведіть 1. Якщо ні, виведіть 2. Помножте це на ...

Split[l]

Розкол l на прогони.

... [[{1,-1}]]

Візьміть перший і останній елемент.

Tr/@ ...

Візьміть загальну кількість обох.

Min[ ... ]

Знайдіть менший між двома.

(Помножте перший результат (1 або 2) на цей результат).


1

Октава, 56 54 байти

 @(n)cummin(d=dec2bin(n)-48)*cummin(flip(d))'*2^!all(d)

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

Пояснення:

d=dec2bin(n)-48

двійкове представлення n

cumd= cummin(d);
cumfd = cummin(flip(d));

Візьміть кумулятивний хв d і кумулятивний хв перевернутийd

res = cumd * cumfd ';

зробити матричне множення

out = res*2^!all(d)

помножте на 2, якщо всі цифри дорівнюють 1;


@FlipTack Дякую, посилання оновлено!
rahnema1

1

Піт, 18 байт

?*FJjQ2lJyhSxR0_BJ

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

Тестовий набір (перший рядок для форматування)

Як це працює

?*FJjQ2lJyhSxR0_BJ  Program. Input: Q
?                   If
  F                 reducing
    jQ2             the binary representation of Q as a list
   J                (store in J)
 *                  by multiplication is truthy:
       lJ            Yield len(J)
                    Else:
          hS         Yield the minimum
            xR0      of the first index of zero
               _BJ   in J and its reverse
         y           * 2
                    Implicitly print

1

APL, 26 байт

+/∘(∧\≢↑(∊⊢(,∧∧)¨⌽))2⊥⍣¯1⊢

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

      ( +/∘(∧\≢↑(∊⊢(,∧∧)¨⌽))2⊥⍣¯1⊢ ) ¨ 0 1 8 23 31 103 127 1877 2015    
0 1 0 2 5 4 7 2 10

Пояснення:

+ / ∘ (∧ \ ≢ ↑ (∊⊢ (, ∧∧) ¨⌽)) 2⊥⍣¯1⊢

                         ⊢ вхід
                    2⊥⍣¯1 перетворити на двійкове подання
   ()
        (⊢ ¨⌽) для кожного біта та відповідного біта з іншого боку
            (∧) приймати логічний і обидва біти,
             , складіть список обох бітів,
              ∧, тоді візьміть і список, і і
         Вирівняти отриманий масив
      ≢ ↑ візьмемо лише перші N біт, де N -
                                довжина оригінального списку бітів
    ∧ \ візьміть логічний запуск і (залишивши лише те
                                початкові)
+ / ∘ суму цих

1

J, 22 байти

(#<.2*(<.&(#.~)|.))@#:

На цьому ґрунтується акуратний трюк, отриманий із цього завдання .

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

Пояснення

(#<.2*(<.&(#.~)|.))@#:  Input: integer n
                    #:  Binary digits of n
(                 )@    Operate on those digits D
               |.         Reverse D
       <.                 Take the minimum of
         &(#.~)           the "trailing truths" of D and reverse(D)
    2*                    Multiply by 2
 #                        The length of D
  <.                      Minimum of length and the previous result

1

PHP, 83 74 байти

3 + 6 байтів, збережених Йоргом

<?=(~$s=decbin($argn))[$a=strspn($s,1)]?min($a,strspn(strrev($s),1))*2:$a;

приймає вхід від STDIN; бігати з -nR.

зламатися

<?=                     # print ...
(~
    $s=decbin($argn)        # $s = binary representation of input
)[
    $a=strspn($s,1)         # $a = number of leading `1`s
]                           # if $s has more than $a digits,
?   min($a,                     # 2. minimum of $a and
        strspn(strrev($s),1)    # 1. number of trailing `1`s
    )*2                         # 3. *2
:   $a                      # else $a (==strlen)

1
<?=~($s=decbin($argn))[$a=strspn($s,1)]?2*min($a,strspn(strrev($s),1)):$a;
Йорг Гюльсерманн

0

JavaScript (ES6), 83 байти

f=x=>(y=x.toString(2),y.match(/^1*$/)?y:([s,e]=y.match(/^1*|1*$/g),s<e?s:e)).length

Безголівки:

function f(n) {
    var binStr = n.toString(2);
    if(binStr.match(/^1*$/)) {
        // If binary representation is all 1s, return length of binary
        return binStr.length;
    } else {
        // Grab the starting and ending 1s in the binary representation
        var [start1s, end1s] = binStr.match(/^1*|1*$/g);
        var startHardness = start1s.length;
        var endHardness = end1s.length;
        return Math.min(startHardness, endHardness);
    }
}

0

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

(h=0;#~IntegerDigits~2//.{{1,m___,1}:>(h+=2;{m}),{1}:>h++};h)&

Чиста функція, де #представляє перший аргумент.

(h=0;...;h)&встановлює h=0, робить купу матеріалів ..., потім повертає h(твердість). Давайте розглянемо купу речей:

#~IntegerDigits~2                                     Binary representation of the input
                 //.                                  Apply the following list of rules repeatedly until there is no change
                    {                                 Start of the list of rules
                     {1,m___,1}                       If you see a list starting and ending with 1 with the sequence m (possibly empty) in between
                               :>(h+=2;{m}),            replace it with just {m} after incrementing h twice.
                                            {1}       If you see the singleton list {1}
                                               :>h++    replace it with h, then increment h.
                                                    } End of the list of rules

Дякую Грегу Мартіну за знайомство з цим фокусом .


0

Haskell , 94 92 байт

b 0=[]
b n=mod n 2:b(div n 2)
h n|(c,_:_)<-span(>0)$zipWith(*)n$reverse n=c++c|1<3=n
sum.h.b

Спробуйте в Інтернеті! Використання:

Prelude> sum.h.b $ 3167
4

Пояснення:
b перетворює число у бінарне і повертає список нуля та одиниць, що мають найменш значущий біт. У hцьому списку обернено і елементарно множиться з початковим списком, а потім span(>0)розбивається після початкових 1s:

       b 3167 = [1,1,1,1,1,0,1,0,0,0,1,1] = n
    reverse n = [1,1,0,0,0,1,0,1,1,1,1,1] = m
zipWith(*)n m = [1,1,0,0,0,0,0,0,0,0,1,1] = z
   span(>0) z = ([1,1],[0,0,0,0,0,0,0,0,1,1])

В результаті чого кортеж шаблон узгоджений з (c,_:_)якої _:_відповідає будь непорожній список, так c = [1,1]. Оскільки байти видаляються спереду і ззаду, c++c = [1,1,1,1]повертається і, нарешті, підсумовується, щоб отримати цифрову твердість .

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


0

Perl, 61 байт

sub f{$_=sprintf('%b',pop);length(/0/?/^(1+).*\1$/&&$1x2:$_)}

Серцем цього є регулярний вираз, /^(1+).*\1$/де відповідь у 2 рази перевищує довжину $1. Решта коду є накладними та розглядає особливий випадок усіх 1.


Ви можете опустити дужки навколо sprintfаргументів. Крім того, використання -pпрапора дозволить вам написати повну програму, яка буде коротшою, ніж ваша функція, оскільки ви зможете опустити її sub f{...}(замість цього вам доведеться закінчити, $_=...але це все-таки покращення на 4 байти). Нарешті, замість своїх length(...), ви можете зробити /0/&&s/^(1+).*\1$/$1$1/;$_=y///c. Це має отримати 51 байт.
Дада


0

CJam, 14 байт

ri2b_0#\W%0#e<

Пояснення:

ri e# Read integer:      | 3167
2b e# Convert to binary: | [1 1 0 0 0 1 0 1 1 1 1 1]
_  e# Duplicate:         | [1 1 0 0 0 1 0 1 1 1 1 1] [1 1 0 0 0 1 0 1 1 1 1 1]
0# e# Index of first 0:  | [1 1 0 0 0 1 0 1 1 1 1 1] 2
\  e# Swap:              | 2 [1 1 0 0 0 1 0 1 1 1 1 1]
W% e# Reverse:           | 2 [1 1 1 1 1 0 1 0 0 0 1 1]
0# e# Index of first 0:  | 2 5
e< e# Minimum:           | 2
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.