现有压缩机的预处理数据

时间:2015-03-31 15:20:43

标签: algorithm haskell compression range

我现有"压缩"算法,将关联压缩为范围。像

这样的东西
type Assoc = [(P,C)]
type RangeCompress :: Assoc -> [(P, [(C,C)]]

将P视为"产品"和C作为"代码"。结果是每个与代码范围列表相关联的产品列表。要查找与给定代码关联的产品,可以遍历压缩数据,直到找到范围,给定代码落入。

如果连续代码可能属于同一产品,则此机制很有效。然而,如果不同产品的代码交错,它们不再形成紧凑的范围,并且我最终得到许多范围,其中上限等于下限和零压缩。

我正在寻找的是一个预压缩器,它会查看原始关联并确定一个足够好的"代码转换,使得根据转换代码表示的关联可以被范围压缩到紧凑范围。像

这样的东西
preCompress :: Assoc -> (C->C)

或更细粒度(按产品)

preCompress ::Assoc -> P -> (C->C)

在这种情况下,产品查询必须首先转换相关代码,然后像以前一样进行查找。因此,转换必须通过少数参数表达,这些参数必须附加到压缩数据,一次用于整个关联或按产品。

我检查了一些压缩算法,但它们似乎都专注于重建原始数据(这里不是严格需要的),同时完全没有关于它们存储压缩数据的方式。但在我的情况下,压缩数据必须是范围,只能通过预压缩的参数进行丰富。

  • 这是一个已知的问题吗?
  • 是否有解决方案?
  • 下一步去哪儿?

请注意:

  • 我主要不想恢复原始数据
  • 我主要对产品查询感兴趣
  • 代码数量为apx 7,000,000
  • 产品数量为apx 200

1 个答案:

答案 0 :(得分:1)

假设每个代码只有一个产品,您可以将数据编码为产品列表(字符串)。让我们假设我们有9个产品(或没有产品)和10个代码随机生成的以下产品和代码列表。

[(P6,C2),
 (P1,C4),
 (P2,C10),
 (P3,C9),
 (P3,C1),
 (P4,C7),
 (P6,C8),
 (P5,C3),
 (P1,C5)]

如果我们按代码对它们进行排序

[(P3,C1),
 (P6,C2),
 (P5,C3),
 (P1,C4),
 (P1,C5),
 -- C6 didn't have a product
 (P4,C7),
 (P6,C8),
 (P3,C9),
 (P2,C10)]

我们可以将其转换为一串产品+一无所有(N)。字符串中的位置决定了代码。

{- C1  C2  C3  C4  C5  C6  C7  C8  C9  C10 -}
[  P3, P6, P5, P1, P1, N , P4, P6, P3, P2   ]

一些字母表中的一串符号(在这种情况下是产品+没有)使我们完全处于充分研究的字符串压缩问题的范围内。

如果我们run-length encode此列表,我们的编码类似于您最初提供的编码。对于每个关联范围,我们存储单个产品+无和运行长度。我们只需要一个小的整数用于运行长度而不是两个(可能是大的)代码。

{-  P3    ,  P6,      P5,      P1, P1,  N    ,  P4,      P6,      P3,      P2    -}
[  (P3, 1), (P6, 1), (P5, 1), (P1, 2), (N, 1), (P4, 1), (P6, 1), (P3, 1), (P2, 1) ]

我们可以将其序列化为一个字节字符串,并使用任何现有的compression libraries字节来执行实际压缩。某些压缩库(例如常用的zlib)位于codec category

以其他方式排序

我们将从之前获取相同的数据,并按产品而不是代码

对其进行排序
[(P1,C4),
 (P1,C5),
 (P2,C10),
 (P3,C1),
 (P3,C9),
 (P4,C7),
 (P5,C3),
 (P6,C2),
 (P6,C8)]

我们希望为每个产品分配新代码,以便产品的代码始终是连续的。我们只是按顺序分配这些代码。

--    v-- New code --v   v -- Old code
[(P1,C1),          [(C1,C4)
 (P1,C2),           (C2,C5),
 (P2,C3),           (C3,C10),
 (P3,C4),           (C4,C1),
 (P3,C5),           (C5,C9),
 (P4,C6),           (C6,C7),
 (P5,C7),           (C7,C3),
 (P6,C8),           (C8,C2),
 (P6,C9),           (C9,C8)]

我们现在有两个要保存的数据。我们在产品和代码范围之间进行(现在)一对一映射,并在新代码和旧代码之间进行新的一对一映射。如果我们按照上一节中的步骤操作,我们可以将新代码和旧代码之间的映射转换为旧代码列表。列表中的位置决定了新代码。

{- C1  C2  C3   C4  C5  C6  C7  C8  C9 -} -- new codes
[  C4, C5, C10, C1, C9, C7, C3, C2, C8  ] -- old codes

我们可以将任何现有的压缩算法抛出此字符串。每个符号在此列表中最多只出现一次,因此传统的压缩机制将不再压缩此列表。这与产品分组和将代码列表存储为产品阵列没有显着差异;新的中间代码只是(更大)指向数组开头和数组长度的指针。

[(P1,[C4,C5]),
 (P2,[C10]),
 (P3,[C1,C9]),
 (P4,[C7]),
 (P5,[C3]),
 (P6,[C2,C8])]

旧代码列表的更好表示可能是旧代码之间的差异。如果代码已经倾向于处于连续范围内,则这些连续范围可以进行行程编码。

{- C4, C5, C10, C1, C9, C7, C3, C2, C8 -} -- old codes
[  +4, +1,  +5, -9, +8, -2, -4, -1, +6  ] -- old code difference

我可能想要存储产品差异以及代码与产品的差异。这应该增加了最终压缩算法压缩的公共子串的机会。

[(+1,[+4,+1]),
 (+1,[+5]),
 (+1,[-9,+8]),
 (+1,[-2]),
 (+1,[-4]),
 (+1,[-1,+6])]