好主意/坏主意:在非常大的数据集上使用Qt的QSet?

时间:2010-12-06 20:27:12

标签: qt memory-management hash dataset compression

使用QSet跟踪一大堆相当大的字符串是不是一个坏主意?每个字符串为54个字符(108个字节)。该集可能包含数千个条目(我还不确定确切的数字)。 QSet仅用于插入和成员资格查询。

如果这是一个坏主意,我绝对愿意接受建议。我的54个字符串仅由6个不同的字符组成(例如“AAAAAAAAABBBBBBBBBCCCCCCCCDDDDDDDDEEEEEEEEEEFFFFFFFFF”)。这似乎是一个很好的压缩候选者,也许?欢迎任何其他建议。

4 个答案:

答案 0 :(得分:3)

意识到通过使用内置集,您将根据数据的性质进行一些路径级压缩。当然,这取决于容器的实现。

查看关于基数树,数字搜索树,红黑树等的一些信息。您将看到您不需要存储每个字符串,而是存储模式。例如,让我们简化您的问题:我们只有3个字符,每个字符最多可以出现2次,每个字符串长度为6个字符。三个可能的字符串是:

AABBCC,AABCBC和AACBCB

通过这些示例,我们可以使用最多6 + 3 + 4 = 13个节点而不是完整的18个节点。没有实质性的,但我不知道你在做什么。与任何类型的压缩一样,您重复使用的前缀模式越多,您拥有的压缩就越多。

编辑: 数字13和18来自路径级压缩。例如,在直接C(用于参数/讨论)中,如果我实现我的字符串存储类作为数组的包装器,我可能只有一个字符指针数组,每个指针引用包含模式的内存中的一个点。在我上面给出的例子中,这将需要18个字符(6 * 3 = 18)。添加数组的大小(假设sizeof(char *)为4,我们的数组将占用3 * 4字节的存储空间= 12 + 18或30字节来存储我们的模式。

如果我将模式存储在某种数字搜索树中,我会做一个小的权衡。我树中的节点将大于1个字节(节点中的字符为1个字节,每个节点中“下一个”指针为4个字节,每个节点为5个字节)。我们存储的第一个模式是AABBCC。这是树中的6个节点。接下来是AABCBC。我们重用第一棵树中的路径AAB,并且只需要另外3个节点用于CBC。最后一种模式是AACBCB。我们重用AA,并且需要4个新节点用于CBCB。这是总共13个节点* 5个字节= 65个字节的存储空间。但是,如果数据前缀中有很多长而重复的模式,那么您将看到一些前缀路径级压缩。

如果您不是这种情况,我会调查Huffman或LZW压缩。这将要求您构建一个包含整数的模式字典。压缩时,构建字典并为文本中的每个模式创建整数id。然后,用整数id替换文本中的模式。解压缩时,你会做相反的事情。我没有时间更详细地描述这些算法,因此您需要查找它们。

这是简单/时间的权衡。如果您的数据允许,请使用较短的方法并使用内置容器。如果没有,您将需要更适合您数据的内容。

答案 1 :(得分:2)

我认为在使用QSet而不是其他类型的容器时会有任何其他问题,例如std :: set,map或vector。如果你想知道内存不足,那可能取决于你需要存储多少千个字符串,以及是否有一种方法可以更简洁地编码它们。 (例如,如果字符始终以相同的顺序出现但相对长度不同,则存储每个字符的长度而不是所有字符。)但是,即使50,000个字符串也只有大约5 MB,其中500,000个只存储50 MB,折扣存储开销,这是现代机器上的适量内存。

答案 2 :(得分:2)

QSet听起来不错。它基本上只是一个哈希表,它可以动态优化其桶大小。完美。

压缩密钥的另一个建议: 将其视为基数为6的数字字符串(假设A = 0,B = 1,... F = 5)并将其转换为二进制(int)。

  QByteArray ba("112"); // instead of "BBC"
  int num = ba.toInt(0, 6 /*base*/); // num == 44

6 ^ 3< 2 ^ 8,因此我们可以用1字节的int(或char)表示字符串中的每3个字符,并对其进行bytearray。这会将密钥的大小从54个字节减少到18个字节。

答案 3 :(得分:1)

从你之前的评论中说:“在我的字符串中,总会有54个字符,每个字符总会有9个字符。顺序是唯一可以改变的字符。”

不要存储原始字符串。你可以将它们压缩成实际使用的6个字符,然后制作一个QSet。一个简单的压缩是{a,b,c,d,e,f},如果预先知道了字符集(并且只有那6个字符),你甚至可以将事物打包成16位整数。