基于多个字段搜索大型数据集的有效方法
Efficient way to search large data set based on several fields
我想知道基于不同字段搜索大型数据集的最佳方法是什么。例如,Person 对象定义如下:
Person:
first name
last name
phone numbers
我有 100k People 类型的对象,我想根据任何字段搜索特定人员?
我尝试使用不同的字段对数据集进行排序,以便我可以在 O(logn) 时间内执行搜索操作,但我知道这不是正确的方法。
这个问题没有一个答案,因为正确的答案(在很大程度上)取决于您对速度与额外存储的关心程度。
如果您想要绝对的最大速度,并且根本不关心使用额外的存储空间,是的,您可以创建数据的三个副本,每个字段一个,输入搜索时,只需使用适当的副本。这可能不像它最初出现的那样可怕。假设每个字符串平均大约 10 个字节,因此结构的整体大小为 ~30 字节。其中 100'000 每个副本大约提供 3 兆字节,总计约 9 兆字节。曾经,这显然是令人望而却步的 - 但是对于一台典型的机器现在至少有8千兆字节的RAM,它并没有那么可怕。
假设你排除了这一点,下一个最明显的可能性是将索引构建到原始数据中——将原始数据放入一个数组中,然后为每个字段构建一个索引,其中索引中的每个条目包含一个字段的数据,以及指向主数据的指针/下标。每个索引条目可以是 ~14 个字节,因此每个索引的大小大约是整体数据的一半。只有三个字段,您不会节省很多,但确实可以节省一些 - 而且复杂性成本极低。使用更多字段,您可以节省更多。
另一种可能性是将索引实现为哈希表。这里的主要优点是您可以避免重复存储日期。例如,如果您计算一个 16 位哈希,每个存储桶有 2 个条目,则可以以 ~512K 字节为单位存储一个索引。如果存储桶已满,但两个条目都与您的输入不匹配,请重新哈希并尝试另一个存储桶。继续前进,直到找到您的物品或找到空桶。
你可以试试Boost.MultiIndex
:
Boost 多索引容器库提供了一个名为 multi_index_container 的类模板,该模板支持构建容器,以维护一个或多个具有不同排序和访问语义的指示:
但是如果你想自己尝试 - 那么最简单的解决方案之一是:对所有数据使用一个容器,此外还维护几个具有适当索引的映射:
class Indixer
{
vector<Record> values; // without specific order
unordered_map<field_type1, Record*> index1; // Search: O(1) average
unordered_map<field_type2, Record*> index2; // Search: O(1) average
map<field_type3, Record*> index3; // Search: O(log N) worst case
public:
// ...
};
您可以使用std::unordered_map
来获得 O(1) 平均访问权限。下面是示例:
#include <initializer_list>
#include <unordered_map>
#include <functional>
#include <algorithm>
#include <iostream>
#include <iterator>
#include <utility>
#include <vector>
#include <string>
using namespace std;
struct Record
{
string first_name, last_name;
};
class Indexer
{
typedef vector<Record> Container;
typedef Record *Handle;
Container values;
unordered_map<string, Handle> first_name_index, last_name_index;
public:
Indexer(Container &&x) : values(move(x))
{
for(auto &x : values)
{
first_name_index[x.first_name] = &x;
last_name_index[x.last_name] = &x;
}
}
const Record &first_name(const string &x)
{
return *first_name_index[x];
}
const Record &last_name(const string &x)
{
return *last_name_index[x];
}
};
int main()
{
vector<Record> v = {{"F1", "L1"}, {"F2", "L2"}};
Indexer x(move(v));
cout << x.first_name("F1").last_name << endl;
cout << x.first_name("F2").last_name << endl;
cout << x.last_name("L1").first_name << endl;
cout << x.last_name("L2").first_name << endl;
}
输出为:
L1
L2
F1
F2
科里鲁的现场演示
- 在C++中初始化向量映射的最有效方法
- 将此布尔值传递给此函数的最有效方法是什么?
- 比较C++变量的最有效方法
- 在 c++ 中解决段树以外的范围查询的有效方法是什么?
- 存储变量的更有效方法是什么?
- 确保套装新鲜度的有效方法
- 当映射包含字符串向量作为值时,从值中获取键的有效方法
- 映射唯一值和重复值的有效方法.可以访问键或值的位置
- 在C++事务之间存储大量字符数据的有效方法
- 在unordered_multimap中精确迭代一次每个键的有效方法
- 一种将 Dart 中的字节数据转换为 C++ 中的无符号字符*的有效方法?
- 检查两个向量是否并行的最有效方法
- 从浮点数中删除小数部分但保留类型的有效方法
- 传递非泛型函数的最有效方法是什么?
- 按升序打印矢量的所有元素直到它为空而没有重复项的最有效方法是什么?
- 创建字符串数组的有效方法
- 返回一个引用C++中另一个类对象的对象的有效方法
- C++去除前x个元素的有效方法,在不改变向量大小的情况下将第x+1个元素推到第一个
- 将一种数据类型的向量复制到同一数据类型的结构向量中的有效方法是什么
- 从std::map值中获取密钥的有效方法