Algorithm 现有压缩机的预处理数据

Algorithm 现有压缩机的预处理数据,algorithm,haskell,compression,range,Algorithm,Haskell,Compression,Range,我有一个现有的“压缩”算法,它将关联压缩到范围中。差不多 type Assoc = [(P,C)] type RangeCompress :: Assoc -> [(P, [(C,C)]] preCompress :: Assoc -> (C->C) 将p读作“产品”,C读作“代码”。结果是一个产品列表,每个产品都与一个代码范围列表相关联。为了找到与给定代码相关联的产品,我们遍历压缩数据,直到找到一个范围,给定代码就落入其中 如果连续代码可能属于同一产品,则此机制工作良好。

我有一个现有的“压缩”算法,它将关联压缩到范围中。差不多

type Assoc = [(P,C)]
type RangeCompress :: Assoc -> [(P, [(C,C)]]
preCompress :: Assoc -> (C->C)
将p读作“产品”,C读作“代码”。结果是一个产品列表,每个产品都与一个代码范围列表相关联。为了找到与给定代码相关联的产品,我们遍历压缩数据,直到找到一个范围,给定代码就落入其中

如果连续代码可能属于同一产品,则此机制工作良好。然而,如果不同乘积的代码交织,它们不再形成紧凑的范围,我最终得到许多范围,其中上界等于下界,压缩为零

我要找的是一个预压缩器,它查看原始关联并确定代码的“足够好”转换,这样以转换后的代码表示的关联可以范围压缩为紧凑的范围。差不多

type Assoc = [(P,C)]
type RangeCompress :: Assoc -> [(P, [(C,C)]]
preCompress :: Assoc -> (C->C)
或更细粒度(按产品)

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

我检查了一些压缩算法,但它们似乎都专注于重建原始数据(这在这里不是严格需要的),同时完全不受存储压缩数据方式的限制。然而,在我的例子中,压缩数据必须是范围,仅通过预压缩的参数来丰富

  • 这是一个已知的问题吗
  • 它有解决办法吗
  • 下一步该去哪里
请注意:

  • 我对恢复原始数据不感兴趣
  • 我主要对产品查找感兴趣
  • 代码数量为apx 7000000
  • 产品数量为apx 200

假设每个代码只有一个产品,您可以将数据编码为产品列表(字符串)。让我们假设我们有以下产品和代码列表,这些产品和代码是从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   ]
一些字母表中的一串符号(在本例中为products+nothing)将我们完全置于经过充分研究的字符串压缩问题领域

如果我们查看此列表,我们的编码与您最初提供的编码类似。对于每个关联范围,我们存储单个产品+零和运行长度。我们只需要一个小整数作为游程长度,而不是两个(可能是大的)代码作为间隔

{-  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) ]
我们可以将其序列化为一个字节字符串,并使用任何现有的on字节来执行实际的压缩。在中可以找到一些压缩库,例如常用的压缩库

以另一种方式排序 我们将从以前获取相同的数据,并按产品而不是按代码对其进行排序

[(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])]

这是一个普遍存在的问题。我重新表述了我的问题,以避免给人留下这样的印象:从严格意义上讲,预压缩程序必须是最佳的;关于
2^26
位。您呈现的编码大约需要
(8+23+23)*2^23=2^29
位。在我的答案中,第一个简单的编码是七倍小。它只需要
2^26
位,如果有运行(您已经怀疑有运行),则需要更少的位。我的答案中的第二个编码需要大约
2^27
位,不需要记录任何关于产品的信息(它记录从代码到代码的函数)。除非你对数据的分布有具体的了解,否则你将无法比
2^26
位做得更好。这个算法似乎和我已经说过的距离压缩有同样的困难。当产品的代码是连续的时,它工作得最好。虽然它允许重建原始数据(我主要不感兴趣),但它不允许我描述的查找。请注意,您在产品字符串中使用P6时出现了一个小错误,但我相信我仍然得到了消息。@MartinDrautzburg构建代码到代码映射的预处理本质上与存储每个产品的所有代码相同。我在回答中添加了另一个部分来说明这一点。