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

Posted

技术标签:

【中文标题】好主意/坏主意:在非常大的数据集上使用 Qt 的 QSet?【英文标题】:Good Idea/Bad Idea: Using Qt's QSet on very large dataset? 【发布时间】:2010-12-06 20:27:11 【问题描述】:

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

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

【问题讨论】:

等等,“宇宙大小”是什么意思?不要告诉我这是你打算拥有的字符串数量 这是解空间。可能字符串的总数(其中许多是重复的)。我不会散列所有这些,我也不想这样做。也许我应该更仔细地措辞我的问题。我会更新 OP。 【参考方案1】:

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

查看有关基数树、数字搜索树、红黑树等的一些信息。您会发现您不需要存储每个字符串,而是存储模式。例如,让我们简化您的问题:我们只有 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,CBCB 需要 4 个新节点。这是总共 13 个节点 * 5 个字节 = 65 个字节的存储空间。但是,如果您的数据前缀中有很多长而重复的模式,那么您会看到一些前缀路径级压缩。

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

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

【讨论】:

我认为这可能是我正在寻找的答案。你能解释一下数字 13 和 18 是从哪里来的吗? @dfetter88 已更新。请参阅我关于前缀压缩与一般压缩的评论。您的数据可能适合也可能不适合您选择的容器。你需要找出你的容器是用什么实现的(链表?二叉搜索树?)并查看你的数据以确定容器的开销是否可以接受。【参考方案2】:

我认为在其他类型的容器(例如 std::set、地图或向量)上使用 QSet 不会有任何额外的问题。如果您想知道内存不足,那可能取决于您需要存储多少个字符串,以及是否有一种方法可以更简洁地对它们进行编码。 (例如,如果字符总是以相同的顺序出现但相对长度不同,则存储每个字符的长度而不是所有字符。)但是,即使这些字符串中的 50,000 个也只有 5 MB 左右,其中 500,000 个存储空间仅为 50 MB,不考虑存储开销,这在现代机器上属于中等内存量。

【讨论】:

想法不错,但恐怕它不适用于我的情况。在我的字符串中,总是有 54 个字符,每个字符总是有 9 个。顺序是唯一改变的东西。【参考方案3】:

QSet 听起来确实是个好主意。它基本上只是一个哈希表,它可以动态优化它的桶大小。完美。

另一个压缩密钥的建议: 将其视为 base-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

【讨论】:

【参考方案4】:

根据您之前的评论:“在我的字符串中,总是有 54 个字符,每个字符总是有 9 个。顺序是唯一改变的东西。”

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

【讨论】:

字符集是预先知道的。它始终是相同的 6 个字符。他们总是有9个。秩序是唯一改变的东西。即便如此,字符串也可以以数万亿种不同的方式进行加扰。当您说“将内容打包成 16 位整数”时,我不确定您是如何暗示这样做的? 我认为问题在于 ChrisV 期望相似字符始终彼此相邻,就好像您可以将它们视为一个字符并稍后将其扩展为多个字符一样。 没错。例如,如果字符集是 ABC,字符串格式是 AABBCC(AA、BB、CC 的顺序可能会发生变化),您可以将所有内容存储在 3 位中:0=AABBCC、1=AACCBB、2=BBAACC,等等开。

以上是关于好主意/坏主意:在非常大的数据集上使用 Qt 的 QSet?的主要内容,如果未能解决你的问题,请参考以下文章

在小数据集上使用 GridSearch 并在大数据集上应用结果是个好主意吗?

在 .NET Core 3.1 和 Entity Framework Core 中处理非常大的请求是一个好主意[关闭]

QT:将我的域对象基于 QObject 是一个好主意吗?

将一个更大的数据库表分成更多表是个好主意吗

将数以亿计的小图像存储到键/值存储或其他 nosql 数据库是个好主意吗?

同时读取多个文件是个好主意吗?