当我们不知道有多少哈希集存在时,在c#中采用两个以上哈希集的交集的最佳方法

时间:2015-02-27 13:35:37

标签: c# c#-4.0 dictionary hashset

我正在为一些大型号码制作一个布尔检索系统。我已经制作了一个哈希集字典的文档,并且这些条目是字典中的条目,并且哈希集包含找到该术语的文档。 现在当我想搜索一个单词时,我只需输入单词,我将使用查询中输入的单词索引字典并打印出相应的散列集。 但我也想搜索句子,在这种情况下,我会将查询拆分成单个单词,并按照这些单词索引字典,现在根据查询中的单词数量,将返回许多哈希集,现在我想要获取这些哈希集的交集,以便我可以返回文档ID,在其中我找出查询中的单词。 我的问题是采用这些散列集的交集的最佳方法是什么?

目前我正在将哈希集放入列表中,然后我将这些n的交集放在一起。一次两个哈希集,然后取前两个结果然后第三个结果等等...

这是代码

Dictionary<string, HashSet<string>> dt = new Dictionary<string, HashSet<string>>();//assume it is filled with data...

while (true)
            {
                Console.WriteLine("\n\n\nEnter the query you want to search");
                string inp = Console.ReadLine();
                string[] words = inp.Split(new Char[] { ' ', ',', '.', ':', '?', '!', '\t' });

                List<HashSet<string>> outparr = new List<HashSet<string>>();
                foreach(string w in words)
                {
                    HashSet<string> outp = new HashSet<string>();
                    if (dt.TryGetValue(w, out outp))
                    {
                        outparr.Add(outp);
                        Console.WriteLine("Found {0} documents.", outp.Count);
                        foreach (string s in outp)
                        {
                            Console.WriteLine(s);
                        }
                    }
                }

                HashSet<string> temp = outparr.First();
                foreach(HashSet<string> hs in outparr)
                {
                    temp = new HashSet<string>(temp.Intersect(hs));
                }

                Console.WriteLine("Output After Intersection:");
                Console.WriteLine("Found {0} documents: ", temp.Count);
                foreach(string s in temp)
                {
                    Console.WriteLine(s);
                }

            }

3 个答案:

答案 0 :(得分:1)

您使用的原则是合理的,但您可以稍微调整一下。

通过对大小的散列集进行排序,您可以从最小的散列集开始,这样就可以最大限度地减少比较次数。

您可以在循环中执行相同的操作,而不是使用IEnumerable<>.Intersect方法,而是使用已经有哈希集的事实。检查哈希集中是否存在值非常快,因此您可以遍历最小集合中的项目并在下一组中查找匹配值,并将它们放入新集合中。

在循环中,您可以在开始时跳过第一个项目。你不需要与自己交叉。

outparr = outparr.OrderBy(o => o.Count).ToList();

HashSet<string> combined = outparr[0];
foreach(HashSet<string> hs in outparr.Skip(1)) {
  HashSet<string> temp = new HashSet<string>();
  foreach (string s in combined) {
    if (hs.Contains(s)) {
      temp.Add(s);
    }
  }
  combined = temp;
}

答案 1 :(得分:1)

IntersectWith是一个很好的方法。像这样:

            HashSet<string> res = null;
            HashSet<string> outdictinary = null;
            foreach(string w in words)
            {
                if (dt.TryGetValue(w, out outdictinary))
                {
                    if( res==null)
                        res =new HashSet( outdictinary,outdictinary.Comparer);
                    else
                    {   
                        if (res.Count==0)
                             break;
                        res.IntersectWith(outdictinary);
                    }
                }
            }
            if (res==null) res = new HashSet();
            Console.WriteLine("Output After Intersection:");
            Console.WriteLine("Found {0} documents: ", res.Count);
            foreach(string s in res)
            {
                Console.WriteLine(s);
            }

答案 2 :(得分:0)

要回答您的问题,您可能会在某一时刻找到一组包含 a,b c 字样的文档和另一个只包含查询中其他单词的集合,这样在几次迭代后交集就会变空。您可以在break。{/ p>之外检查此foreach。{/ 1}

现在,恕我直言,做这个交叉点没有意义,因为通常搜索结果应该包含按相关性排序的多个文件。 它也会更容易,因为您已经有一个包含一个单词的文件列表。从每个单词获得的哈希值中,您必须计算文件ID的出现次数,并返回有限数量的ID,这些ID按出现次数递减。