Розшифруйте величину змінної довжини


16

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

Спосіб роботи досить простий. Як велика-ендіанська серія байтів, найзначніший біт (MSB) кожного байта - це 1вказівка ​​на те, що наступний байт VLQ слідує. Решта 7 біт кожного байту становлять декодоване значення.

Приклад (з Вікіпедії):

[ 0x86, 0xc3, 0x17 ] => 106903

введіть тут опис зображення Додаткові посилання: Wikipedia , Some Guy .

Виклик:

Враховуючи величину змінної довжини, перетворіть її на ціле число.

Вхід:

Список від одного до чотирьох байтів або тип 32-бітного значення, що представляє дійсний VLQ цілого числа.

Вихід:

Ціле значення вводу VLQ.

Правила та оцінка:

  • Це код-гольф, тому найкоротша відповідь у байтах за кожну мову виграє.
  • Застосовуються стандартні правила та правила вводу / виводу за замовчуванням .
  • Лазівки заборонені (звичайно).
  • Будь ласка, надайте посилання з тестом для вашого коду ( TIO.run тощо).
  • Дуже рекомендується чітке пояснення вашої відповіді.
  • Вбудовані модулі , які обробляють це перетворення не заборонені, проте НЕ їх використання є набагато цікавішим.

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

Input (VLQ)                   Output (int)

[                   0x00 ] => 0
[                   0x07 ] => 7
[                   0x7f ] => 127
[             0x81, 0x00 ] => 128
[             0xC0, 0x00 ] => 8192
[             0xff, 0x7f ] => 16383
[       0x81, 0x80, 0x00 ] => 16384
[       0x86, 0xc3, 0x17 ] => 106903
[       0xbd, 0x84, 0x40 ] => 1000000
[       0xff, 0xff, 0x7f ] => 2097151 
[ 0xC0, 0x80, 0x80, 0x00 ] => 134217728  
[ 0xFF, 0xFF, 0xFF, 0x7F ] => 268435455  

Примітка: від вас не потрібно використовувати шістнадцяткові букви, щоб представляти байт як ваш вхід чи вихід. Ви можете використовувати десятковий буквальний ( [ 129, 128, 0 ]), ціле число (0x80818000 ) або будь-яке інше розумне байт / октетне представлення, якщо воно краще підходить для вашої платформи. Формат є гнучким, якщо він становить 1-4 байт / октет.

Гольф геть!


Чи гарантовано останній байт введення <128? Або нам потрібно підтримувати такі випадки, як [0x01, 0x80, 0x02] => 1?
Арнольд

5
@Arnauld Я зараз краще бачу те, до чого ти потрапляв, і в ретроспективі справа, яку ти згадуєш, була б добрею, щоб цього вимагали. Наприклад, у MIDI ви будете читати потік даних, тому ви не обов'язково знаєте, який байт є останнім. Як написано, гарантуючи, що останній байт у списку - останній байт у VLQ, робить MSB нерелевантним і насправді начебто перемагає призначення VLQ. Як і в, ви не обов'язково зможете використовувати ці (дійсні для виклику) процедури для декодування файлів MIDI. Зовсім моє погано! Мав би тримати це в пісочниці набагато довше ...
640 КБ

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

1
@KevinCruijssen, величина змінної довжини повинна бути потоком байтів, де технічно ви знаєте лише, коли вона закінчується, досягнувши маркера MSB = 0. Я знаю, що правила не дуже захоплювали це, але за визначенням VLQ мені доведеться сказати "ні".
640

1
@gwaugh Добре, це має сенс, і я можу зрозуміти міркування. Я відповідно зміню свою відповідь MathGolf. :)
Kevin Cruijssen

Відповіді:





4

J , 10 байт

128#.128|]

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

Отримавши натхнення у відповіді APL J Salle.

  • 128|] Залишок вхідних чисел ділиться на 128
  • 128#. Інтерпретується як цифри базового 128 числа


3

05AB1E , 6 байт

žy%žyβ

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

12827


Так, цей вбудований нині марний. У застарілій версії я бачив певну користь для цього лише тоді, коли перед вами у програмі є цифра. Інакше ви могли просто використовувати 7o. Сьогодні ви можете стиснути певні 3-байтові цілі числа (діапазон [101,355]) у 2 байти, тому 128 ƵRвсе одно може бути . Я також цікавився того ж, що стосується 2-байтової вбудованої версії для 16 .. Зазвичай ви просто використовуєте буквальне або інакше у нас буде 4o/ 4n/ якщо в програмі є цифра. Тільки коли цифра до 16, що, я думаю, не відбудеться, вбудований корисний ..
Кевін Круїйсен



2

APL + WIN, 22 байти

Підказки для вектора цілих чисел:

2⊥¯32↑,0 1↓⍉(8⍴2)⊤¯4↑⎕

Спробуйте в Інтернеті! Надано Dyalog Classic

Пояснення:

¯4↑⎕ Pad the vector to 4 integers from the right.

⍉(8⍴2)⊤ Convert to a matrix of 8 bit values.

,0 1↓ drop the MSBs and flatten to a vector.

2⊥¯32↑ pad bit vector to 32 bits starting at LSB and convert back to integer.

2

Стакс , 12 байт

ü╫ôà¡k2Wù}a☺

Запустіть і налагодьте його на staxlang.xyz!

Розпаковано (14 байт) та пояснення:

rk128%128i^#*+
r                 Reverse 'cuz big-endian
 k                Fold from the left using block:
  128%              Modulize by 128
      128i^#        Push 128 to the power of (one plus the iteration index)
            *       Multiply
             +      Add to the total
                  Implicit print

Stax має вбудовану базову конверсію, але вона працює лише на рядках. Він майже працює на списках цілих чисел; проблема полягає в поводженні Стакса 0.

Рядок - це список цілих чисел. Коли ви використовуєте такий список як рядок, будь-які нулі автоматично перетворюються на 32, як приємна скорочення для пробілів. Оскільки вбудований |bдля базової конверсії трактує операнд як рядок, а не як необроблений список цілих чисел, будь-який випадок із нулем вийде з ладу.

10 байт, не вдається на нулі

Ç┘_A♥∙QZ►╣
{128%m128|b    Unpacked
{128%m         Modulize each by 128
      128|b    Convert from base 128

Запустіть і налагодьте його на staxlang.xyz!


1
Я бачу, звідки взялося це питання. {:B7)m$:bпакети до 8, і, здається, працює також, хоча це екзотичне використання $.
рекурсивна

@recursive Це розумно. Опублікуйте це як окрему відповідь.
Khuldraeseth na'Barya


2

C (gcc) , 48 байт

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

f(i,j){for(j=0;j|=i&127,i&128;j<<=7,i>>=8);i=j;}

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

C (gcc) , 53 байти

Якщо потрібен байтовий масив:

f(c,j)char*c;{for(j=0;j|=*c&127,*c++&128;j<<=7);c=j;}

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


Коли я складаю ваш код тестування TIO за допомогою -Os, він обробляє лише останні два випадки. Я не знаю достатньо С, щоб сказати, чому.
qwr

Типовим параметром @qwr TIO є те -O0, що дозволяє (як правило) зберігати повернене значення в перший параметр. Це хитрість ^ Wfeature в коді з гольфу, але не працює з більш високими рівнями оптимізації.
ErikF

Ви можете голити байт, замінивши &128на >>7.
Г. Сліпен

2

MathGolf , 14 байт

ê♣%hrx♣▬^mÅε*Σ

Введіть як цілі числа.

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

У мене є відчуття, що це може бути коротшим. Це трохи прикро, що MathGolf має вбудований 1-байт для постійної 128, але не перетворення бази (крім бінарної / шістнадцяткової).

Пояснення:

ê              # Take the inputs as integer-array
 ♣%            # Take modulo-128 on each value in this array
   h           # Push the length of the array (without popping the array itself)
    r          # Pop and push a list in the range [0, length)
     x         # Reverse the array to (length, 0]
      ♣▬       # Take 128 to the power of each value in this array
        ^      # Zip the two lists together to create pairs
         mÅ    # Map each pair to (using the following two commands):
           ε*  #  Reduce by multiplication (so basically the product of the pair)
             Σ # After multiplying each pair, take the total sum
               # (after which the entire stack joined together is output implicitly)

Базова конверсія проводиться на столі з першого дня, але є деякі інші ремонти щодо перетворення базових даних, які я хочу одночасно вирішити. Наприклад, я не хочу, щоб різні оператори конвертували в / з шістнадцяткових рядків.
maxb

2

Пітон 3 , 58 49 байт

-9 байт завдяки @Chas та @ ar4093

lambda x:int("".join(bin(a|128)[3:]for a in x),2)

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

або

lambda x:int("".join(f"{a%128:07b}"for a in x),2)

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

Введіть через список цілих чисел.

binФункція Python додає "0b" на початок рядка, тому їх потрібно зняти, перш ніж їх можна об'єднати. Він також не зберігає провідні нулі, тому якщо таких немає (він же останній байт), їх потрібно додати ще. добре.Завдяки @Chas за те, що завжди встановивши перший біт, я можу просто видалити перші три символи і зробити це.

Мабуть (згідно @ ar4093), formatфункція дозволяє не тільки не мати префікса "0b", але також видаляти перший біт і додавати до 7 символів одночасно.


Ласкаво просимо до CGCC, що зробить вас набагато гіршим програмістом! :). Я спочатку думав так само, як і ви. Ви можете зберегти 9 байт за допомогою того bin(a|128)[3:], що вам тоді не потрібно zfill.
Час Браун

Як ви вже говорили, за допомогою функції форматування ви можете зберегти 9 байт: bin(a)[2:].zfill(8)[1:]->f"{a%128:07b}"
ar4093


1

Japt , 10 8 байт

Вводить введення як масив цілих чисел.

muIÑ ìIÑ

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

Збережено 2 байти, взявши натхнення з розчину алефальфи .

muIÑ ìIÑ     :Implicit input of integer array
m            :Map
 u           :  Modulo
  I          :    64
   Ñ         :    Multiply by 2
     ì       :Convert to decimal
      IÑ     :  From base 64*2



1

Пакет Windows, 76 байт

:a
@set/ax="%1&127",y=y*128+x
@if %2. neq . @shift&goto:a
@echo %y%

Параметри передачі з префіксом "0x" і пробіл між (наприклад, 0xC0 0x80 0x80 0x00).


Чудово зроблено. Очевидно, що хтось ще тестує його, вам потрібно переконатися, що потрібно робити @set y=,ax=між прогонами.
640

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