如何使这段代码更快?

时间:2012-05-08 11:11:24

标签: c# performance data-mining

我的功能对我的任务来说非常慢(它必须快10到100倍)

这是代码

public long Support(List<string[]> sequences, string[] words)
{

            var count = 0;
            foreach (var sequence in sequences)
            {
                for (int i = 0; i < sequence.Length - words.Length + 1; i++)
                {
                    bool foundSeq = true;
                    for (int j = 0; j < words.Length; j++)
                    {
                        foundSeq = foundSeq && sequence[i + j] == words[j];
                    }
                    if (foundSeq)
                    {
                        count++;
                        break;
                    }
                }
            }

            return count;
}

public void Support(List<string[]> sequences, List<SequenceInfo> sequenceInfoCollection)
{
    System.Threading.Tasks.Parallel.ForEach(sequenceInfoCollection.Where(x => x.Support==null),sequenceInfo =>
    {
        sequenceInfo.Support = Support(sequences, sequenceInfo.Sequence);
    });

}

其中List<string[]> sequences是一组单词数组。该数组通常包含250k +行。每行约4-7个字。 string[] words是我们试图计算的一个单词数组(所有单词至少包含一次序列)。

问题是foundSeq = foundSeq && sequence[i + j] == words[j];。此代码占用了大部分执行时间(Enumerable.MoveNext在第二位)。我想哈希我的数组中的所有单词。数字比字符串快,对吧?我认为它可以帮助我获得30%-80%的性能。但我需要10倍!我该怎么办?如果你想知道它是apriory算法的一部分。


支持函数检查单词序列是否是序列列表中任何序列的一部分并计算多少次。

3 个答案:

答案 0 :(得分:2)

Knuth-Morris-Pratt算法

在计算机科学中,Knuth-Morris-Pratt字符串搜索算法(或KMP算法)通过采用当发生不匹配时的观察来搜索主“文本字符串”S内“单词”W的出现。本身体现了足够的信息来确定下一场比赛的开始位置,从而绕过对先前匹配的角色的重新检查。

该算法由Donald Knuth和Vaughan Pratt于1974年构思,并由James H. Morris独立构思。三人于1977年联合出版。


来自维基百科:https://en.wikipedia.org/wiki/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm

这是您应该做的改进之一。差别很小:代码中的“单词”是算法术语中的“字符”;你的“单词”数组就是KMP中的一个单词。

这个想法是当你搜索“abc def ghi jkl”并且已经匹配“abc def ghi”时,但是下一个单词不匹配,你可以跳三个位置。

Search:   abc def ghi jkl
Text:     abc def ghi klm abc def ghi jkl
i=0:      abc def ghi jkl?
skip 2:       XXX XXX  <--- you save two iterations here, i += 2
i=2:                  abc?
i=3:                      abc? ...

答案 1 :(得分:0)

我要做的第一个优化是早期失败。即使你知道它的失败,你的内部循环也会继续贯穿整个序列,并且你正在做一些不必要的布尔逻辑。你的代码是:

    for (int j = 0; j < words.Length; j++)
    {
        foundSeq = foundSeq && sequence[i + j] == words[j];
    }

相反,只需这样做(或等效):

    for (int j = 0; j < words.Length; j++)
    {
        if (sequence[i + j] != words[j])
        {
            foundSeq = false;
            break;
        }
    }

这将为您节省大部分比较(如果不匹配,您将退出第一个单词,而不是在您知道结果为假时继续比较)。如果您希望每个序列中单个单词的出现率较低(例如,如果您在英文文本页面中找到句子),它甚至可以使您正在寻找十倍的差异。

答案 2 :(得分:0)

理论上,您可以连接每个序列并使用子字符串匹配。我现在手头没有编译器,所以我无法测试它是否会真正提高性能,但这是一般的想法:

List<string> sentences = sequences.Select(seq => String.Join(" ", seq));
string toMatch = String.Join(" ", words);

return sentences.Count(sentence => sentence.Contains(toMatch));