4

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

我想找到出现在最多书签中的关键字。

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

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

该算法非常简单:

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

使用Jon Skeet 的 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

4 回答 4

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"
于 2012-08-12T07:49:13.940 回答
2

你不需要遍历整个宇宙。想法是创建一个查找和跟踪最大值。

    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;
    }
于 2012-08-12T07:49:14.630 回答
2

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

我正在考虑这些方面的事情。内存要求要高得多,而且由于我没有对任何东西进行基准测试,它可能会更慢,或者没有帮助,但如果它不适合你,我会删除我的答案。

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;
于 2012-08-12T07:55:20.360 回答
1

在 python 中 50 毫秒:

>>> 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
>>>
于 2012-08-12T12:25:43.010 回答