解码可变长度数量


16

可变长量(也被称为VLQuintvar)是编码最多只能使用尽可能多字节根据需要一个28位整数值的方法。MIDI文件格式中使用了此格式,以最小化某些事件数据的大小。

它的工作方式非常简单。作为字节的高位优先级序列,每个字节的最高有效位(MSB)为a 1,表示随后是另一个VLQ字节。每个字节的其余7位组成解码值。

示例(来自维基百科):

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

在此处输入图片说明 其他参考资料:Wikipedia,《Some Guy》

挑战:

给定一个可变长度的数量,将其转换为整数值。

输入:

一到四个字节或32位值类型的列表,表示整数的有效VLQ。

输出:

VLQ输入的整数值。

规则和评分:

  • 这是代码高尔夫,因此每种语言的最短答案以字节为单位
  • 标准规则默认I / O规则适用。
  • 禁止漏洞(当然)。
  • 请提供测试代码的链接( 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
@Arna现在我能更好地了解您的意思了,回想起来,您提到的情况将是很好的要求。例如,在MIDI中,您将读取数据流,因此不必知道哪个字节是最后一个字节。它的写方式保证了列表中的最后一个字节是VLQ中的最后一个字节,从而使MSB无关紧要,并且实际上有悖于VLQ的目的。如上所示,您不一定能够使用这些(对挑战有效)例程进行MIDI文件解码。完全是我的坏人!应该把它放在沙盒中的时间更长……
640KB,

5
这也部分是我的坏处,因为我认为我已经看到了沙箱中的挑战并且没有给予足够的重视。但是,是的,这就是我的初衷:给定一个字节列表,要么输出第一个VLQ值,要么输出所有VLQ值
阿纳尔德

1
@KevinCruijssen,可变长度量应该是字节流,从技术上讲,您只能通过到达MSB = 0标记知道它何时结束。我知道规则并没有完全抓住这一点,但是根据VLQ的定义,我将不得不拒绝。
640KB

1
@gwaugh好的,这很有意义,我可以理解其原因。我将相应地修改MathGolf答案。:)
凯文·克鲁伊森

Answers:





4

J,10个字节

128#.128|]

在线尝试!

从J Salle的APL答案中汲取灵感。

  • 128|] 输入数字的余数除以128
  • 128#. 解释为基数为128的数字


3

05AB1E,6个字节

žy%žyβ

在线尝试!

12827


是的,这种内建功能如今已无用。在旧版本中,只有在程序中有数字之前,我才能看到它的某些用途。否则,您可以使用7o。如今,您可以将某些3字节整数(范围[101,355])压缩为2个字节,因此ƵR无论如何都可以压缩128个字节。.我也想知道内置16字节2字节的方法是否相同。通常,您只需要使用文字,否则我们就得4o/ 4n/ 如果数字是它背后的程序。只有当一个数字是16,我不认为会发生之前,内置的是有用的..
凯文Cruijssen


2

JavaScript(ES6),29个字节

-2个字节,感谢@Shaggy

将输入作为字节数组。

a=>a.map(p=c=>p=p<<7|c&127)|p

在线尝试!


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

Stax,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具有内置的基本转换,但仅适用于字符串。不过,它几乎可以处理整数列表。问题出在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很聪明。将其作为单独的答案发布。
库勒德斯·纳巴里


2

C(gcc),48个字节

以big-endian顺序的整数作为输入,该顺序与字节数组的顺序相同。

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

在线尝试!


当我使用-Os编译您的TIO测试代码时,它仅处理最后两种情况。我对C的了解不足,无法说明原因。
qwr

@qwr TIO默认为-O0,这允许您(通常)将返回值存储到第一个参数中。这是代码打高尔夫球中的一个古怪的功能,但不适用于更高的优化级别。
ErikF

您可以通过更换剃去一个字节&128>>7
G. Sliepen

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)

从第1天开始就开始进行基本转换,但是我想同时解决一些其他有关基本转换的问题。例如,我不想让不同的运算符在十六进制字符串之间进行转换。
maxb

2

Python 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)

在线尝试!

通过整数列表输入。

Python的bin函数在字符串的开头添加了“ 0b”,因此必须先将其删除才能进行连接。它也不会保留前导零,因此,如果没有任何前导零(aka最后一个字节),则必须重新添加。如果有前导零(又是除最后一个字节以外的所有字节),则必须将其删除好。感谢@Chas弄清楚了通过始终设置第一位,我可以删除前三个字符并完成操作。

显然(根据@ ar4093) format函数不仅允许不具有'0b'前缀,而且还允许删除第一位并同时填充至7个字符。


欢迎来到CGCC,这将使您成为更糟糕的程序员!:)。起初我和你有同样的想法。您可以节省9个字节,bin(a|128)[3:]因为您不需要zfill
Chas Brown

如您所说,使用格式功能可以保存9个字节:bin(a)[2:].zfill(8)[1:]->f"{a%128:07b}"
ar4093



1

木炭,11字节

I↨﹪θ¹²⁸¦¹²⁸

在线尝试!链接是详细版本的代码。将输入作为数组。说明:

   θ        Input array
  ﹪ ¹²⁸    Elementwise modulo 128
 ↨      ¹²⁸ Convert from base 128
I          Cast to string for implicit print


1

Windows Batch,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=在两次运行之间进行一次。
640KB

1
仅使用“ set y =“就足够了。
彼得·费里
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.