【问题标题】:best way to take an intersection of more than two hashsets in c#, when we donot know before hand how many hashsets are there当我们事先不知道有多少哈希集时,在 C# 中取两个以上哈希集的交集的最佳方法
【发布时间】:2026-01-06 02:50:01
【问题描述】:

我正在为一些大数字制作一个布尔检索系统。文档,我在其中制作了一个哈希集字典,字典中的条目是术语,哈希集包含找到该术语的文档ID。 现在,当我想搜索单个单词时,我只需输入该单词,然后我将使用查询中输入的单词索引字典并打印出相应的哈希集。 但是我也想搜索句子,在这种情况下,我会将查询拆分为单个单词并按这些单词索引字典,现在取决于查询中的单词数,现在将返回许多哈希集我将想要取这些哈希集的交集,以便我可以返回文档 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);
                }

            }

【问题讨论】:

  • 所以你的代码目前可以工作?那么这个问题会更适合 CodeReview.SE
  • 如果在任何文档中都找不到任何单词,outparr.First() 会抛出异常吗?

标签: c# c#-4.0 dictionary hashset


【解决方案1】:

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

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

除了使用IEnumerable&lt;&gt;.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;
}

【讨论】:

    【解决方案2】:

    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);
                }
    

    【讨论】:

    • HashSet 中捕获Comparer 可能是合理的,以保持相同的相等含义:res=new HashSet(outdictinary,outdictinary.Comparer)
    【解决方案3】:

    为了回答您的问题,您可能会在某一时刻找到一组包含单词 a、bc 的文档,而另一组仅包含查询中的其他单词,因此交集在几次迭代后可能变为空。您可以从foreach 中检查这个和break

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

    【讨论】: