在下面的代码中,我有许多字符串(DNA序列)
我存储在矢量中。我有一个struct
,read_tag
用于识别每个字符串; read_tag.read_id
是字符串标识符。我将每个字符串的30个字符子串用作unordered_multimap
中的一个键,并将read_tag
作为值;目的是对共享30个字符序列的字符串进行分组。当然,相同的字符串将散列为相同的值,并最终位于多映射中的同一个存储桶中。偏移用于从30个字符标记的索引零开始“移位”。
但是,当我运行此代码时,遍历每个桶;我发现同一个桶中有多个不同的序列。我认为碰撞是在unordered_mutlimap
中解决的,因此在一个桶中,它们应该只是一个键(字符串)。我知道碰撞可能发生,但我认为链接,探测等都是在unordered_mutlimap
中实现的。
您应该能够运行并检查输出以查看我感到困惑的地方。
我还std::hash
每个密钥,一个桶中的密钥,我发现“冲突”中的密钥具有不同的哈希值。
因此,就好像发生碰撞一样,导致带有差异的值。键在同一个桶中,但矛盾的是,键被散列到不同的val。 他们是一种避免这种情况的方法,并根据存储桶中的密钥来区分值吗? 或者我需要实现这个吗?
#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;
}
}
答案 0 :(得分:1)
是的,你是对的。不能保证,两个不同的物品落在两个不同的桶中。你只知道,两个相同的物品落在同一个桶里。
解决问题的方法就是避免使用存储桶。类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;
}
答案 1 :(得分:0)
所以,对于任何有兴趣的人。我在标准中找到了这个
[C ++ 11:23.2.5 / 5]:如果容器的key_equal函数对象在传递这些值时返回true,则认为Key类型的两个值k1和k2是等效的。如果k1和k2是等价的,则散列函数应为两者返回相同的值。 [..]
[C ++ 11:23.2.5 / 8]:无序关联容器的元素被组织成桶。具有相同哈希码的密钥出现在同一个存储桶中。 [..]
因此,具有相同密钥的两个值将始终在同一个存储桶中,但具有不同值的密钥也可能最终存在于这些存储桶中。所以,我认为实施可能更聪明,并且实际上促进了这些情况;我能想到它的一个原因是保持桶数量下降。您可以从输出中看到填充的桶是稀疏的;并且我们越接近直接地址表(向量数组,由哈希索引),我们最终会得到一个巨大的潜在密钥世界,其中有大量的空槽,哈希表可以防止它们。所以,它似乎是一个合理的空间优化。
因此,我选择使用multimap
。原因
就是这样,multimap
中的值是基于键排序的,所以我可以基于键对单个值进行分组。在unordered_multimap
中,一旦我到达一个桶(在O(1)中因为它是一个哈希表),就没有基于密钥的排序,所以我不能通过桶进行线性传递来对序列进行分组。