数据采用浓缩格式

时间:2012-09-26 08:34:11

标签: c# algorithm

我需要一个能够帮助我以简洁格式保存和查询数据的库(本质上是一个迷你DSL),这里是我想要的样本:

更新1 - 请注意,上面示例中的数字很小,只是为了更容易遵循逻辑,实际数字受限于c# long类型容量,例如: 1,18,28,29,39,18456789,18456790,18456792,184567896

示例原始数据集:1,2,3,8,11,12,13,14

简明样本数据集 1..3,8,11..14

能够将1,2,4,5,6,7,8,9,10作为1..10-3呈现的绝对好处。

查询示例数据集:

查询1(获取范围): 1..5 - > 1..3

查询2(检查值是否存在) ?2 - > true

查询3(获取多个范围和标量值): 1..5,11..12,14 - > 1..3,11..12,14

我不想从头开始开发它,并且非常喜欢使用已经存在的东西。

2 个答案:

答案 0 :(得分:1)

我不知道任何现成的库可以满足您的需求,但我不确定您是否需要它。

我建议您考虑使用现有的BitArray课程。如果,如您的示例所示,您对压缩小整数集感兴趣,那么单个BitArray(例如256位)可以表示[0..255]范围内的任何整数集。当然,如果您的典型集合中只有5个整数,那么这种方法实际上会扩展您的存储需求;你必须根据自己对集合的了解来确定这些数组的正确大小。

我建议您将数据视为整数集,因此您的示例1,2,3,8,11,12,13,14将通过设置BitArray中的相应位来表示。然后,您的查询操作会缩减为测试BitArray与您的数据BitArray之间的交集。

顺便提一下,我认为你的变换2 -> true的例子2最好留在将整数集映射到整数集的函数域中,即它应该变换2 -> 2。如果你愿意,写一个返回布尔值的不同方法。

我猜你需要编写代码将整数打包到BitArrays并将BitArrays解包为整数,但这是压缩成本的一部分。

答案 1 :(得分:1)

以下是自从我阅读你的问题以来我曾经有过的一些想法。我无法确定它们中的任何一个是否真的适用于您的用例,但我希望您能在这里找到有用的东西。

存储压缩数据

您可以采取的措施来减少数字在磁盘上占用的空间:

  • 如果您的值介于1到10M之间,请不要使用long,请使用uint。 (每个数字4个字节。)
  • 实际上,请勿使用uint。将数字7位存储到一个字节,剩下的位用于表示“此数字中有更多字节”。 (然后1-127将适合1个字节,128~16k,2个字节,~16k~~ 2M,3个字节,~2M- ~270M,4个字节。)

这应该将您的存储空间从每个数字的8个字节(如果您最初将它们存储为long s)减少到平均3个字节。此外,如果您最终需要更大的数字,可变字节存储将能够保存它们。

然后我可以想出几种方法来进一步减少它,因为你知道数字总是在增加并且可能包含大量的运行。只有通过对实际数据进行尝试才能知道哪种方法最适合您。

  • 对于您的每个实际数字,请存储两个数字:数字本身,后跟其后连续的数字(例如2,3,4,5,6 => 2,4)。你必须存储单独的数字,例如8,0因此会增加存储空间,但如果您的数据有很多运行(特别是长数据),这应该会平均减少存储空间。您可以进一步在运行中存储“单个间隙”,例如1,2,3,5,6,7 => 1,6,4(明确为4太小而不能成为下一次运行的开始)但这会使处理变得更复杂,并且不会节省太多空间,所以我不会打扰。
  • 或者,不是存储数字本身,而是存储增量(3,4,5,7,8,9 => 3,1,1,2,1,1。这将减少用于存储较大数字的字节数(例如15000,15005 (4个字节)=> 15000,5(3个字节))。此外,如果数据包含大量运行(例如大量1个字节),则它将很好地压缩(例如zip)。

代码处理

我只是建议您编写一些方法,将文件从磁盘流式传输到IEnumerable<uint>(如果最终得到更大的数字,则为ulong),然后执行相反的操作。处理你从上面实现的任何内容。

如果你以懒惰的方式执行此操作 - 使用yield return在从磁盘读取数据时返回数字并计算它们,并将数字流式传输到磁盘而不是将它们保存在内存中并立即返回它们无论存储数据的大小如何,都可以降低内存使用率。

(我,但我不是确定,即使是GZipStream和其他压缩流也可以让您流式传输数据而无需全部在记忆中。)

查询

如果您要比较两个大数据集,我不会建议使用LINQ的Intersect方法,因为它需要将其中一个源完全读入内存。但是,如您所知,两个序列都在增加,您可以编写一个类似的方法,只需要为每个序列保存一个枚举器。

如果您根据用户输入的小数字列表查询您的一个数据集,您可以愉快地使用当前实现的LINQ Intersect方法,因为它只需要序列完全在内存中。