更快地计算项目出现的集合数量?

时间:2012-08-12 07:02:13

标签: c# algorithm optimization hashset

我有一个书签列表。每个书签都有一个关键字列表(存储为HashSet)。我还有一组所有可能的关键词(“宇宙”)。

我想找到大多数书签中出现的关键字。

我有1356个书签,总共有698,539个关键字,其中187,358个是唯一的。

如果我遍历Universe中的每个关键字并计算它出现的书签数量,我就会进行254,057,448次检查。我的机器需要35秒。

算法非常简单:

var biggest = universe.MaxBy(kw => bookmarks.Count(bm => bm.Keywords.Contains(kw)));

使用Jon Skeet's MaxBy

我不确定是否可以加快速度,但有什么我可以做的吗?也许以某种方式并行化它?


dtb的解决方案需要不到200毫秒来构建宇宙并找到最大的元素。这么简单。

var freq = new FreqDict();
foreach(var bm in bookmarks) {
    freq.Add(bm.Keywords);
}
var biggest2 = freq.MaxBy(kvp => kvp.Value);

FreqDict只是我在Dictionary<string,int>之上构建的一个小课程。

4 个答案:

答案 0 :(得分:4)

您可以获取所有关键字,对其进行分组,并获得最大的群组。这会占用更多内存,但应该更快。

我尝试了这个,在我的测试中它快了大约80倍:

string biggest =
  bookmarks
  .SelectMany(m => m.Keywords)
  .GroupBy(k => k)
  .OrderByDescending(g => g.Count())
  .First()
  .Key;

试运行:

1536 bookmarks
153600 keywords
74245 unique keywords

Original:
12098 ms.
biggest = "18541"

New:
148 ms.
biggest = "18541"

答案 1 :(得分:2)

您无需遍历整个Universe。想法是创建一个查找并跟踪最大值

    public Keyword GetMaxKeyword(IEnumerable<Bookmark> bookmarks)
    {
        int max = 0;
        Keyword maxkw = null;

        Dictionary<Keyword, int> lookup = new Dictionary<Keyword, int>();

        foreach (var item in bookmarks)
        {
            foreach (var kw in item.Keywords)
            {
                int val = 1;

                if (lookup.ContainsKey(kw))
                {
                    val = ++lookup[kw];
                }
                else
                {
                    lookup.Add(kw, 1);
                }

                if (max < val)
                {
                    max = val;
                    maxkw = kw;
                }
            }
        }

        return maxkw;
    }

答案 2 :(得分:2)

我没有您的样本数据,也没有进行任何基准测试,但我会采取措施。可以改进的一个问题是大多数bm.Keywords.Contains(kw)检查都是未命中的,我认为可以避免这些检查。最受限制的是给定书签的任何一个关键字集合(即:它通常比宇宙小得多)所以我们应该从那个方向开始而不是从另一个方向开始。

我正在思考这些问题。内存需求要高得多,因为我没有对任何事情进行基准测试,它可能会更慢,或者没有帮助,但如果它不适合你,我会删除我的答案。

Dictionary<string, int> keywordCounts = new Dictionary<string, int>(universe.Length);
foreach (var keyword in universe)
{
    keywordCounts.Add(keyword, 0);
}

foreach (var bookmark in bookmarks)
{
    foreach (var keyword in bookmark.Keywords)
    {
        keywordCounts[keyword] += 1;
    }
}

var mostCommonKeyword = keywordCounts.MaxBy(x => x.Value).Key;

答案 3 :(得分:1)

在python中

50ms:

>>> import random

>>> universe = set()
>>> bookmarks = []
>>> for i in range(1356):
...     bookmark = []
...     for j in range(698539//1356):
...         key_word = random.randint(1000, 1000000000)
...         universe.add(key_word)
...         bookmark.append(key_word)
...     bookmarks.append(bookmark)
...
>>> key_word_count = {}
>>> for bookmark in bookmarks:
...     for key_word in bookmark:
...         key_word_count[key_word] = key_word_count.get(key_word, 0) + 1
...

>>> print max(key_word_count, key=key_word_count.__getitem__)
408530590

>>> print key_word_count[408530590]
3
>>>