Encoding 编码随机可变长度二进制码序列的最紧凑的方法?

Encoding 编码随机可变长度二进制码序列的最紧凑的方法?,encoding,binary,integer,variable-length,space-efficiency,Encoding,Binary,Integer,Variable Length,Space Efficiency,假设您有一个列表,您希望以尽可能紧凑的方式将其编码为二进制形式 我不在乎读写性能。我只想使用最小的空间。另外,这个例子是用Java编写的,但我们并不局限于Java系统。每个“列表”的长度是无限的。因此,任何编码每个列表长度的解决方案本身都必须编码可变长度的数据类型 与此问题相关的是可变长度整数的编码。您可以将每个列表看作一个可变长度的无符号整数 请仔细阅读问题。我们不仅限于Java系统。 编辑 我不明白为什么很多答案都在谈论压缩。我并没有试图做压缩本身,但只是编码位的随机序列下来。除此之外,每个

假设您有一个
列表
,您希望以尽可能紧凑的方式将其编码为二进制形式

我不在乎读写性能。我只想使用最小的空间。另外,这个例子是用Java编写的,但我们并不局限于Java系统。每个“列表”的长度是无限的。因此,任何编码每个列表长度的解决方案本身都必须编码可变长度的数据类型

与此问题相关的是可变长度整数的编码。您可以将每个
列表
看作一个可变长度的
无符号整数

请仔细阅读问题。我们不仅限于Java系统。

编辑


我不明白为什么很多答案都在谈论压缩。我并没有试图做压缩本身,但只是编码位的随机序列下来。除此之外,每个位序列都有不同的长度和顺序需要保留

你可以用另一种方式思考这个问题。假设您有一个随机无符号整数(无界)的任意列表。如何将此列表编码为二进制文件?

研究 我读了一些书,发现我真正想要的是

结果 我将使用本文中描述的一种变体


我现在明白了小整数的表示是如何与大整数进行权衡的。只要选择第一个整数表示为“大”的通用代码,就可以在需要对任意大整数进行编码时,从长远来看节省大量空间。

您可以将每个列表转换为一个位集,然后序列化位集。

我想,为了“尽可能紧凑的方式”,您需要一些压缩,但哈夫曼编码可能不是一种可行的方法,因为我认为它最适用于具有静态每符号频率的字母表

签出-它在位上运行,可以适应动态输入概率。我还看到,有一个BSD许可证,它将为您做这件事,似乎期待单位作为输入


我认为,为了实现最大压缩,您可以将每个内部列表(以其长度为前缀)连接起来,然后在整个批次上再次运行编码算法。

我看不出编码任意一组位与压缩/编码任何其他形式的数据有什么不同。请注意,您只对正在编码的位施加了一个宽松的限制:即,它们是位列表的列表。有了这个小小的限制,这个比特列表就变成了数据,任意数据,这就是“正常”压缩算法压缩的内容

当然,大多数压缩算法都假设输入在将来(或过去)以某种方式重复,如LZxx系列压缩器,或具有给定的符号频率分布

考虑到您的先决条件和压缩算法的工作原理,我建议您执行以下操作:

  • 使用尽可能少的字节数打包每个列表的位,使用字节作为位字段,编码长度等
  • 在生成的字节流上尝试huffman、算术、LZxx等
  • 有人可能会说,这是一种非常明显且最简单的方法,但这不会起作用,因为您的位序列没有已知的模式。但事实是,这是在任何情况下都能做到的最好的

    除非,你从你的数据中知道了什么,或者这些列表上的一些转换使它们产生了某种模式。以JPEG编码中DCT系数的编码为例。列出这些系数的方式(对角线和之字形)有利于变换不同系数的输出模式。这样,传统的压缩可以应用于结果数据。如果你知道一些比特列表,允许你以更可压缩的方式(一种显示更多结构的方式)重新排列它们,那么你将得到压缩。

    理论极限

    这是一个很难回答的问题,如果你不了解更多你打算压缩的数据;您的问题的答案可能因不同领域而异

    例如,来自:

    无损数据压缩算法不能保证对所有输入数据集进行压缩。换句话说,对于任何(无损)数据压缩算法,都会有一个在算法处理时不会变小的输入数据集。这是很容易证明与初等数学使用计数参数

    基本上,由于理论上不可能无损地压缩所有可能的输入数据,所以甚至不可能有效地回答您的问题

    实际妥协


    只需使用Huffman、DEFLATE、7Z或一些类似ZIP的现成压缩算法,并将这些位作为可变长度字节数组(或列表、向量,或在Java或任何您喜欢的语言中调用的任何内容)进行enocde。当然,读回这些位可能需要一些解压缩,但这可以在幕后完成。您可以创建一个隐藏内部实现方法的类,以返回某个索引范围内的布尔值列表或数组,尽管数据在内部存储在包字节数组中。在一个或多个给定索引处更新布尔值可能是一个问题,但绝非不可能。

    我对Java了解不多,因此我想我的解决方案必须是通用的:)

    1。压缩列表

    由于布尔运算效率低下,每个
    列表
    都应该压缩成
    列表
    ,很简单,只需一次抓取8个即可

    最后一个“字节”可能不完整,因此您当然需要存储编码的位数

    2。序列化元素列表

    你有两种方法来帮助你
    0... .... > this byte encodes the number of items (7 bits of effective)
    10.. .... / .... .... > 2 bytes
    110. .... / .... .... / .... .... > 3 bytes
    
    // Tricky here, we indicate how many bits are used, but they are packed into bytes ;)
    List<Boolean> list = [false,false,true,true,false,false,true,true]
    encode(list) == [0x08, 0x33] // [00001000, 00110011]  (2 bytes)
    
    // Easier: the length actually indicates the number of elements
    List<List<Boolean>> super = [list,list]
    encode(super) == [0x02, 0x08, 0x33, 0x08, 0x33] // [00000010, ...] (5 bytes)
    
    booleans = ceil( n / 8 )
    
    length = 1   for 0    <= n < 2^7   ~ 128
    length = 2   for 2^7  <= n < 2^14  ~ 16384
    length = 3   for 2^14 <= n < 2^21  ~ 2097152
    ...
    length = ceil( log(n) / 7 )  # for n != 0 ;)
    
    bytes =
     if n == 0: 1
     else     : ceil( log(n) / 7 ) + ceil( n / 8 )
    
    0  -> 0 0
    1  -> 0 1
    2  -> 10 10
    3  -> 10 11
    4  -> 110 100
    5  -> 110 101
    8  -> 1110 1000
    16 -> 11110 10000 (=> 1 byte and 2 bits)
    
    # Original scheme
    length = ceil( ( log(n) / 7)
    
    # New scheme
    length = 2 * ceil( log(n) )
    
    n         -> bits spared
    [0,1]     ->  6
    [2,3]     ->  4
    [4,7]     ->  2
    [8,15]    ->  0    # Turn point
    [16,31]   -> -2
    [32,63]   -> -4
    [64,127]  -> -6
    [128,255] ->  0    # Interesting eh ? That's the whole byte effect!
    
    len('01010101') = 8 -> 1000
    len('1000')     = 4 -> 100
    len('100')      = 3 -> 11
    len('11')       = 2 -> 10
    
    encode('01010101') = '10' '0' '11' '0' '100' '0' '1000' '1' '01010101'
    
    len(encode('01010101')) = 2 + 1 + 2 + 1 + 3 + 1 + 4 + 1 + 8 = 23
    
    threshold     2    3    4    5      My proposal
    -----------------------------------------------
    [0,3]    ->   3    4    5    6           8
    [4,7]    ->   10   4    5    6           8
    [8,15]   ->   15   9    5    6           8
    [16,31]  ->   16   10   5    6           8
    [32,63]  ->   17   11   12   6           8
    [64,127] ->   18   12   13   14          8
    [128,255]->   19   13   14   15         16
    
    17 -> '10 100 10001 0'
    
    11000101 10010110 00100000
    
       10001 01001011 00100000
    
    head  | value
    ------+------------------
    00001 | 0110100111000011
    
    head  | length | value
    ------+--------+-----------
    00001 | 1001   | 011011001
    
    Start traversing the input list
    For each sublist:
        Output 0xFF 0xFE
        For each item in the sublist:
            Output the item as a stream of bits, LSB first.
              If the pattern 0xFF appears anywhere in the stream,
              replace it with 0xFF 0xFD in the output.
            Output 0xFF 0xFC
    
    If the stream has ended then end any previous list and end reading.
    Read bits from input stream. If pattern 0xFF is encountered, read the next 8 bits.
       If they are 0xFE, end any previous list and begin a new one.
       If they are 0xFD, assume that the value 0xFF has been read (discard the 0xFD)
       If they are 0xFC, end any current integer at the bit before the pattern, and begin reading a new one at the bit after the 0xFC.
       Otherwise indicate error. 
    
    let encode2d(list1d::Bs) = encode1d(length(list1d), true) @ list1d @ encode2d(Bs) encode2d(nil) = nil let encode1d(1, nextIsValue) = true :: nextIsValue :: [] encode1d(len, nextIsValue) = let bitList = toBoolList(len) @ [nextIsValue] in encode1d(length(bitList), false) @ bitList let decode2d(bits) = let (list1d, rest) = decode1d(bits, 1) in list1d :: decode2d(rest) let decode1d(bits, n) = let length = fromBoolList(take(n, bits)) in let nextIsValue :: bits' = skip(n, bits) in if nextIsValue then bits' else decode1d(bits', length) assumed library functions ------------------------- toBoolList : int -> bool list this function takes an integer and produces the boolean list representation of the bits. All leading zeroes are removed, except for input '0' fromBoolList : bool list -> int the inverse of toBoolList take : int * a' list -> a' list returns the first count elements of the list skip : int * a' list -> a' list returns the remainder of the list after removing the first count elements
    byte 255 - escape code
    byte 254 - begin block
    byte 253 - list separator
    byte 252 - end block
    
     struct {
        int nmem; /* Won't overflow -- out of memory first */
        int kind; /* 0 = number, 1 = recurse */
        void *data; /* points to array of bytes for kind 0, array of bigdat for kind 1 */
     } bigdat;
    
     int serialize(FILE *f, struct bigdat *op) {
       int i;
       if (op->kind) {
          unsigned char *num = (char *)op->data;
          for (i = 0; i < op->nmem; i++) {
             if (num[i] >= 252)
                fputs(255, f);
             fputs(num[i], f);
          }
       } else {
          struct bigdat *blocks = (struct bigdat *)op->data
          fputs(254, f);
          for (i = 0; i < op->nmem; i++) {
              if (i) fputs(253, f);
              serialize(f, blocks[i]);
          }
          fputs(252, f);
     }