通过local_it在bucket中迭代时,unordered_multimap中的冲突
Collisions in unordered_multimap when iterating through bucket via local_it
在下面的代码中,我有许多字符串(DNA序列)我正在存储在一个向量中。我有一个struct
、read_tag
,我用它来标识每个字符串;read_tag.read_id
是字符串标识符。我取每个字符串的30个字符的子字符串,并将其用作unordered_multimap
中的键,以read_tag
为值;其目的是对共享30个字符序列的字符串进行分组。自然,相同的字符串将散列到相同的值,并最终在多映射中的同一个桶中。偏移量用于给出30个字符标记的索引零的"偏移"。
然而,当我运行这段代码时,会遍历每个bucket;我发现在同一个桶中有多个不同的序列。我认为冲突是在unordered_mutlimap
中解决的,因此在一个bucket中,它们应该只是一个键(字符串)。我知道可能会发生冲突,但我认为链接、探测等都是在unordered_mutlimap
中实现的。您应该能够运行并检查输出,看看我在哪里感到困惑。
我还std::hash
每个键,一个在一个bucket中,我发现"冲突"中的键有一个不同的哈希值。
因此,就好像发生了冲突,导致在同一个bucket中具有不同密钥的值,但矛盾的是,密钥被散列到不同的vals。他们有没有办法避免这种情况,并根据bucket中的键来区分值?或者我需要实现它吗?
#include <iostream>
#include <string>
#include <unordered_map>
#include <vector>
#include <functional>
using namespace std;
int main() {
vector<string> reads;
reads.push_back("CCAGCTGCTCTCACCCTGGGCAGGGTCCCTGCACACACTGTATCTTTTGAGGTCCCTTCAGGACCCCGGTTTGCTGCCTC");
reads.push_back("CCAGCTGCTCTCACCCTGGGCAGGGTCCCTGCACACACTGTATCTTTTGAGGTCCCTTCAGGACCCCGGTTTGCTGCCTC");
reads.push_back("GGCAGGGTCATACCCGATTAACTTGTTATAGAGTATGGGGCATCAACTTGGGCAGCAATGGGGAACGGTGTCTCTGGAAG");
reads.push_back("CCAGCTGCTCTCACCCTGGGCAGGGTCCCTGCACACACTGTATCTTTTGAGGTCCCTTCAGGACCCCGGTTTGCTGCCTC");
reads.push_back("GGCAGGGTCATACCCGATTAACTTGTTATAGAGTATGGGGCATCAACTTGGGCAGCAATGGGGAACGGTGTCTCTGGAAG");
reads.push_back("GGCAGGGTCATACCCGATTAACTTGTTATAGAGTATGGGGCATCAACTTGGGCAGCAATGGGGAACGGTGTCTCTGGAAG");
reads.push_back("GGCAGGGTCATACCCGATTAACTTGTTATAGAGTATGGGGCATCAACTTGGGCAGCAATGGGGAACGGTGTCTCTGGAAG");
reads.push_back("CCGGGCGTGGTGGCGTGCACCTGTAATCCCAGCTACTTGGGATGTTCAGGCAGGAGACTCGCTTGATCCCCGGGGACGGA");
reads.push_back("CCGGGCGTGGTGGCGTGCACCTGTAATCCCAGCTACTTGGGATGTTCAGGCAGGAGACTCGCTTGATCCCCGGGGACGGA");
reads.push_back("CCGGGCGTGGTGGCGTGCACCTGTAATCCCAGCTACTTGGGATGTTCAGGCAGGAGACTCGCTTGATCCCCGGGGACGGA");
reads.push_back("CCGGGCGTGGTGGCGTGCACCTGTAATCCCAGCTACTTGGGATGTTCAGGCAGGAGACTCGCTTGATCCCCGGGGACGGA");
reads.push_back("CCAGCTGCTCTCACCCTGGGCAGGGTCCCTGCACACACTGTATCTTTTGAGGTCCCTTCAGGACCCCGGTTTGCTGCCTC");
struct read_tag{
unsigned int read_id; // unique string identifier
int offset; // shift of 30 character substring represented by tag
};
unordered_multimap<string, read_tag> mutation_grouper;
for(int read_id=0; read_id < reads.size(); read_id++) {
string read = reads[read_id];
for(int i=0; i < read.size()-30; i++) {
string sub_read = read.substr(i, 30);
read_tag next_tag;
pair<string, read_tag> key_val;
next_tag.read_id = read_id;
next_tag.offset = i;
key_val.first = sub_read;
key_val.second = next_tag;
mutation_grouper.insert(key_val);
}
}
cout << "mutation_grouper buckets" << endl;
std::hash<std::string> hash_er;
for(unsigned int bucket = 0; bucket < mutation_grouper.bucket_count(); bucket++) {
cout << "Bucket: " << bucket << endl;
for( auto local_it = mutation_grouper.begin(bucket);
local_it != mutation_grouper.end(bucket); ++local_it) {
cout << local_it->first << " : " << local_it->second.read_id
<< ", " << local_it->second.offset << ", " << endl;
cout << "hash value: " << local_it->first <<"::: " << hash_er(local_it->first) << endl;
}
cout << endl << endl;
}
}
是的,您的回答是正确的。两个不同的物品放在两个不同桶里是不能保证的。你只知道,两个相同的东西落在同一个桶里。
解决你的问题的方法就是避免使用水桶。类unordered_multimap
(以及multimap
)具有方法equal_range
,该方法为您提供具有特定键的元素范围。因此,您只需要对所有键进行迭代,并使用equal_range
对所有值进行迭代。遗憾的是,没有一种方法可以让你迭代键,所以你必须有点棘手。以下代码应该为您提供所需的输出:
// iterate through all elements in the multimap
// don't worry, we'll skip a bunch
for (auto it = mutation_grouper.begin(); it != mutation_grouper.end(); )
{
// Get the range of the current key
auto range = mutation_grouper.equal_range(it->first);
// Print all elements of the range
cout << it->first << endl;
for (auto local_it = range.first; local_it != range.second; ++local_it)
std::cout << " " << local_it->second.read_id << " " << local_it->second.offset << 'n';
// Step to the end of the range
it = range.second;
}
所以,对于任何感兴趣的人来说。我在标准中找到了这个
[C++11:23.2.5/5]:如果容器的Key_equal函数对象在传递这些值时返回true,则Key类型的两个值k1和k2被认为是等效的。如果k1和k2相等,则哈希函数应为两者返回相同的值。[..]
[C++11:23.2.5/8]:无序关联容器的元素被组织到桶中。具有相同哈希代码的密钥出现在同一存储桶中。[..]
所以,具有相同关键字的两个值总是会出现在同一个存储桶中,但具有不同值的关键字也可能出现在这些存储桶中。因此,我认为实施可能更明智,并实际促进这些情况;我能想到的一个原因是减少水桶的数量。您可以从输出中看到,填充的桶是稀疏的;我们越接近直接地址表(由哈希索引的向量数组),我们就会得到一个巨大的潜在密钥宇宙,以及大量的空槽,哈希表可以防止这些空槽。因此,这似乎是一个合理的空间优化。
因此,我选择使用multimap
。原因就是说,multimap
中的值是根据关键字排序的,所以我可以根据关键字对值进行分组。在unordered_multimap
中,一旦我到达一个bucket(在O(1)中,因为它是一个哈希表),就没有基于键的排序,所以我不能通过bucket对序列进行线性分组。
- 使用std::multimap迭代器创建std::list
- std::multimap<std::chrono::milliseconds, T>::rbegin 在 MSVS-13 中指向 end()?
- C++在使用std::multimap时出现运行时错误的几率很小
- 用 C++20 的给定键读取/写入 std::multimap 的所有值?
- std::multimap::erase() 在迭代时
- 访问 std::multimap 值的最佳方式?
- MAP和Multimap之间的C 模板专业化
- 如何使用 std::multimap 将整数键映射到两个用作多维数组坐标的整数值(对于井字)?
- "std::multimap"是否保证每个键的实际值在相等范围内?
- 无法插入qcustomplot :: qcpgraph中的Multimap C
- 特定键在 std::multimap 中出现的次数
- swig:std/multimap.i似乎打破了TCL包装
- 如何使用"std::multimap"或任何其他容器对多个值进行排序?
- c++ 为什么 std::multimap 比 std::p riority_queue 慢
- 检查multimap c++中是否已经存在值
- std :: multimap :: emplace覆盖旧键
- 是否可以<int>在一行中将向量值分配给 std::unordered<int,std::vector<int>>?
- 初始化静态std :: multimap在其中声明的方法
- ISO C 禁止没有类型的“ Multimap”声明
- 我从std :: multimap.lower_bound获得了无效的指针