压缩稀疏位数组

4
我有一个大小为1024字节(8192位)的数组,其中大部分都是零。
在0.01%到10%之间的位将被设置(随机,无模式)。
考虑到缺乏结构和相对较小的大小,这些如何进行压缩?
(我的第一个想法是存储已设置位之间的距离。每个距离需要13位,但在最坏情况下,10%的占用需要13 * 816/8 = 1326字节,这并没有改进。)
这是为超低带宽通信而设计的,因此每个字节都很重要。

1
字节中可能的值是什么?还是信息在位中? - Art
如何在一个字节中编码高达254的距离?如果是255,则添加下一个字节。这对于距离> 255的低概率情况可能是一种改进。 - Yunnosch
或者将数值编码为220以内的数字,超过220的部分(减去220)乘以220,然后加上下一个字节。 - Yunnosch
1
@chrisdew 坚持使用8位可以避免制定比特流协议。但你是对的。与其发明新东西,研究现有的工作可能是最好的选择。 - Yunnosch
1
从我的经验来看,编码距离是一个不好的想法,但这可能与我的应用有关,因为我不想从磁盘解码数据,而只是将其原样使用。(我现在正在撰写更长的答案)。 - Art
显示剩余6条评论
1个回答

4
我曾深入研究过类似的问题,但我的数据集要大得多(每个集合中有1到3000万个元素,共有3000万个可能值),因此它们都更适合压缩,并且与数据大小相比,压缩元数据微不足道。我从未将东西压缩到小于的单位,因此如果您开始将13位值分成几部分,则下面写的内容可能不适用。感觉应该可以工作,但请谨慎行事。
我发现的有效方法是采用几种策略,这些策略取决于我们所拥有的特定数据。好消息是,每个集合中元素的数量是确定哪种压缩策略最适用于特定集合的很好指标。因此,您需要的所有元数据就是集合中元素的计数。在我的数据格式中,第一个且唯一的元数据值(我将不具体说明,仅称其为“值”,您可以根据自己的需要将其压缩为字节、16位值或13位值)是集合中元素的计数,其余部分只是对集合元素进行编码。
这些策略包括:
  1. 如果集合中只有很少的元素,那么你无法比使用数组更好。例如:[3, 1, 4711, 8140]。在这种情况下,数据被编码为数组。

  2. 如果几乎所有元素都在集合中,那么你可以跟踪不在集合中的元素。例如:[8190,17,42]。

  3. 如果集合中大约一半的元素,则几乎无法找到更好的方法,除了位图。因此,你会得到[4000,{bitmap}]。这是唯一一种数据长度比严格未压缩的情况更长的情况。

  4. 如果集合中的元素比“很少”多,但比“大约一半”少,我发现另一种策略。将集合中可能的值的位分成两半。假设我们有2^16(更容易描述,应该适用于2^13)个可能的值。这些值被分为256个范围,每个范围有256个可能的值。然后,我们有一个包含256个字节的数组,每个字节描述了每个范围中有多少个值(所以字节0告诉我们有多少个元素是[0,255],字节1给出[256,511]等等)。紧随其后的是每个范围中值模256的数组。这里的技巧是,虽然集合中的每个元素编码为数组(策略1)都将是2个字节,但在此方案中,每个元素仅为1个字节+ 256个静态字节,用于元素计数。这意味着,一旦我们的集合中有超过256个元素,通过从策略1切换到4,我们可以通过节省空间来实现。

  5. 策略4可以被优化(如果你的数据是随机的,那么可能没有意义,但我的数据有时会有更多的模式,所以对我有用)。由于在先前的编码中每个元素仍需要8位,因此一旦元素的子数组超过32个元素(256字节),我们就可以将其存储为位图。这也是在策略4/5和3之间切换策略的好断点。如果该策略中的所有数组都只是位图,则应该使用策略3(它比这更复杂,但是策略之间的断点可以预先计算得非常精确,以便每次都选择最可能有效的策略)。

我只是大致尝试了保存集合中数字之间的差异。快速实验表明,它们并不比我上面提到的策略更有效,具有不可预测的退化情况,但最重要的是,我使用的应用程序真的很喜欢不必反序列化其数据,直接从磁盘(mmap)中使用原始数据。


谢谢,好的答案 - 我会采用多策略方法。在接受答案之前,我将等待几天并在此期间自己工作。在存储数字时,您是否使用任何通用代码 https://en.wikipedia.org/wiki/Universal_code_(data_compression)?位之间的间隙似乎会聚集在8192 /占用周围 - 这个事实有帮助吗? - fadedbee
我将尝试存储距离和8192/占用之间的差异,并使用Golomb编码(使用交织负数)进行编码。 - fadedbee

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接