【问题标题】:Optimal way for storing compressed sets存储压缩集的最佳方式
【发布时间】:2017-06-13 01:55:34
【问题描述】:

正如标题所说,我正在寻找将集合存储在内存中的最佳方式。我只对字节集感兴趣(从0255 的整数数组,其中顺序并不重要)。不要求编码/解码速度很快。唯一需要的是集合应该需要尽可能少的内存。

我想出的第一个方法是为每个集合分配256 位数组(32 字节),n 位置的位告诉集合中是否有n。这种方法的问题在于,即使集合大部分是空的(只有很少的元素),它也需要相同数量的内存。

我尝试的第二种方法是将集合存储为常规数组。因此,如果一个集合包含n 元素,那么它将需要存储n + 1 字节。第一个字节表示元素的数量,其他字节表示元素。但是,正如我们所知,集合中的顺序并不重要,所以有些东西强烈地告诉我,必须有一种方法来改善这一点。

我的第三次尝试是枚举所有可能的集合并只存储集合的索引(整数表示它在所有可能的字节集合列表中的索引)。但是,事实证明它与第一种方法完全等价。基本上,我仍然需要32 字节来存储任何集合,所以它不是很有用。

我的第四次尝试是基于我的第二种方法。我注意到该集合包含n 元素,当然,它需要n + 1 字节(如果我使用第二种方法)。但是,例如,如果元素 k 出现在集合中(实际上是在数组中,因为在我的第二次尝试中我将集合存储为数组),那么它就不能再次出现。基本上,如果k 再次出现,那么它一定意味着不同的东西(可能是k - 1)。所以,我做了一些优化,我注意到如果我对每个下一个元素进行不同的编码,我可以节省一些字节(例如,[3, 3, 5, 7] 被解释为元素为{3, 4, 5} 的一组3 元素(每个下一个元素减少它的索引)和[3, 3, 5, 6]被解释为{3, 4, 2}(注意34已经存在,所以6减少2变成4,但是4存在并且@ 987654346@ 存在,所以它必须是2))。但是这种方法如何才能真正节省字节呢?我进行了实验并意识到我可以对数组中的元素进行排序,以便在某些情况下避免使用高位对元素进行编码,因此我为每个元素保存了 1 位,大约节省了 n / 16 字节(这是n / 2 * 1 / 8)。

我提出的第五种方法与我的第二种方法相似,但它对数字 fo 元素的解释不同。如果元素的数量小于128,那么它通常会从内存中的以下数组中读取所有元素。但是,如果元素的数量大于128,那么它会创建一个完整的集合,然后从内存中的以下数组中删除元素。平均而言,它节省了很多字节,但距离最优还差得很远。

我的最后一次尝试(第六次尝试)是仅枚举一些集合(例如,创建一个集合列表,其中将包含:完整集合、仅包含偶数的集合、仅包含奇数的集合、包含小于 128 个元素的集合、设置大于 128 的元素等),然后使用该列表中的元素和基本集合操作(​​联合、交集等)来重建原始集合。对于我们从列表中使用的每个基本集,它将需要几个字节,并且需要几个位来进行联合或交集操作,当然还有一个字节来表示序列的长度。这很大程度上取决于基本集合列表中应该硬编码的元素数量,但似乎很难预先创建和正确选择该列表中的元素。不管怎样,有件事告诉我这不是很聪明的方法。

但是帽子实际上是最优化的方式?有人告诉我,我的第四次尝试并没有那么糟糕,但我们能做得更好吗?我使用的集合具有随机数量的元素,因此平均每个集合有128 个元素,所以我正在寻找一种方法来为每个集合分配128 位(16 字节)。到目前为止,我做得最好的是使用离我的目标很远的第四种方法。

再次提一下,速度并不重要。编码/解码可能非常慢,唯一重要的是集合需要尽可能少的内存。当我说“在内存中”时,我的意思是在内存中编码(压缩)。此外,我对尽可能少的位(不仅仅是字节)感兴趣,因为我想在我的 HDD 上存储数十亿个压缩集,因此计算每个集所需的平均位数很重要,这样我就知道有多少资源可用于我想要实现的目标。

附:如果你想要一些代码(但我真的不明白你为什么要这样做),我可以在这里发布我用 C 语言为所有这些方法制作的解决方案。无论如何,我不是在询问如何在特定的编程语言中实现这一点的代码或技术细节,我只是在询问用于压缩集合的方法/算法。

提前谢谢你。

【问题讨论】:

  • 你知道集合大小的分布吗?你似乎觉得大多数套装都很小;是这样吗,还是你只是认为你应该能够在小系列上做得更好,即使它们很少见?
  • 集合中是否存在任何偏差,即某些值或值组合是否比其他值更可能出现?
  • 您如何计算“2^256 位”相当于 32 个字节?它比那个大很多......稍后的“2^128 位”也是如此。
  • @rici。元素的数量是随机分布的。对于0 - 255 中的每个数字n,一个集合包含n 元素的概率相同。我不认为具有 “更少元素” 的集合应该需要更少的内存。但是,平均而言,应该可以不为每个集合使用32 位。
  • @m69。不幸的是没有。元素和元素数量是随机分布的。

标签: c arrays algorithm set compression


【解决方案1】:

您的第一种方法(以及等效的第三种方法)已经是最优的。无法改进。

您正在使用 2256 组可能的数字。根据鸽巢原理,您需要 2256 个数字来识别它们,并且需要 256 位来表示这些数字。识别使用少于 256 位的集合的任何方法都会使至少一对(可能还有很多对)集合共享相同的标识符。

【讨论】:

    【解决方案2】:

    有 2^256 个可能的字节集。

    如果所有集合的可能性相同,那么您能做的最好的事情就是使用一个恒定的 256 位(32 字节)来指示您拥有 2^256 种可能性中的哪一种。

    您似乎不喜欢这个想法,因为您认为只有几个元素的集合应该占用更少的位。但是,如果它们发生的可能性不高于任何其他集合,那么这将不是最佳的。

    如果元素较少的集合更有可能,那么使用恒定的 32 字节不是最优的,但最优编码取决于可能集合的精确概率分布,你没有给。信息论的相关概念是“熵”:https://en.wikipedia.org/wiki/Entropy_(information_theory)

    简而言之,在最佳编码中,所需的平均位数将是所有 2^256 个可能集合的 Sum_of_all Pᵢ * -log₂(Pᵢ),其中每个 Pᵢ strong> 是必须对特定集合进行编码的概率(所有 Pᵢ 必须总和为 1)

    如果元素的数量是唯一你认为应该影响编码大小的东西,那么你不能用这样的东西走得太远:

    1) 使用 1 个字节写出集合中元素的数量。有 257 种可能的集合大小,但您可以将 0 用于 0 和 256 个元素。

    2) 在所有具有该长度的集合的枚举中写出集合的索引。 (如果你写了一个 0 那么你需要 1 位来表示空集或完整集)。如果已知该集合有 N 个元素,则该数字所需的位数将为 log₂(256!/(N!*(256-N)!)

    【讨论】:

    • 但是有超过 2^248 个长度为 110 ~ 146 的集合,因此结合这些集合需要超过 256 位的长度。
    • 是的,确实如此。如果所有集合的编码长度相同,则最多只能有 256 位。如果你让一些更短,那么你必须让其他人更长。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-27
    • 2014-11-09
    • 1970-01-01
    • 2023-03-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多