【问题标题】:Iterating over multi-thousand element List迭代数千个元素列表
【发布时间】:2016-05-29 14:10:38
【问题描述】:
case 15: {
    for (int i = 0; i < words.Count; i++) {
        if (words[i].Length == 8) {
            var tupled = words[i].ConcatCheck();
            for (int n = 0; n < word.Count; n++)
                if (word[n] == tupled.Item1 || word[n] == tupled.Item2)
                    temp++;
        }
        if (temp >= 2)
            matches.Add(words[i]);
        temp = 0;
    }
    break;
}

它的作用:
第一个“for 循环”遍历包含大约 248000 个元素长的词的 List,检查长度为 8 的词。 当找到一个时,我通过调用 ConcatCheck() 方法(我为 obj String 编写的扩展方法)创建了单词的前半部分和后半部分(每半部分 4 个字母)的 Tuple。那部分又快又好。

真正需要工作的是第二个“for 循环”。每一个 8 个字母的单词都会激活这个循环,它会遍历更大的 List 大约 267000 个元素,检查 Tuple 的两个项目是否存在。如果两者都找到,我会将原始单词添加到“匹配”列表中。

这部分需要将近 3 分钟才能找到我拥有的 248k 字典中的所有匹配项。有什么方法可以优化/加速它?

【问题讨论】:

  • 专门检查 Linq 和 Plinq
  • 目标?:返回一个仅包含唯一单词的列表?返回每个唯一单词及其出现次数?目标到底是什么? word 是什么,它总是相同的输入词还是每 X 次迭代都会改变?
  • 那么......你想找到由两个 4 个字母单词组成的所有 8 个字母单词吗?

标签: c# list for-loop


【解决方案1】:

如果您只是想检查某个词是否存在于集合中,请使用HashSet 而不是ListArrayHashSet 类针对 Contains 检查进行了优化。

示例

使用以下代码,我在不到 50 毫秒内找到了 english dictionary (github version) 中由两个 4 字母单词组成的所有 8 个字母单词。

WebClient client = new WebClient();
string dictionary = client.DownloadString(
    @"https://raw.githubusercontent.com/dwyl/english-words/master/words.txt");

Stopwatch watch = new Stopwatch();
watch.Start();

HashSet<string> fourLetterWords = new HashSet<string>();

using (StringReader reader = new StringReader(dictionary))
{
    while (true)
    {
        string line = reader.ReadLine();
        if (line == null) break;
        if (line.Length != 4) continue;

        fourLetterWords.Add(line);
    }
}

List<string> matches = new List<string>();

using (StringReader reader = new StringReader(dictionary))
{
    while (true)
    {
        string line = reader.ReadLine();
        if (line == null) break;
        if (line.Length != 8) continue;

        if (fourLetterWords.Contains(line.Substring(0, 4)) &&
            fourLetterWords.Contains(line.Substring(4, 4)))
            matches.Add(line);
    }
}

watch.Stop();    

为什么你的代码这么慢?

for (int n = 0; n < word.Count; n++)
    if (word[n] == tupled.Item1 || word[n] == tupled.Item2)
        temp++;

这部分是罪魁祸首之一。不是检查Are both parts contained in my array?,而是检查Are 2 or more of my 2 words contained in an array?

找到这两个词后,您可以通过打破循环来优化这部分。

if (word[n] == tupled.Item1 || word[n] == tupled.Item2)
    if(++temp >= 2) break;         

可以通过按长度或字母顺序对单词进行预排序来进行进一步优化(取决于您运行此搜索的频率)。

【讨论】:

  • 这是一个绝妙的解决方案。效果很棒!我还尝试将我使用的 List 换成 HashSet,即使这样也大大提高了速度。
【解决方案2】:

O(n) 使用字典:

            IList<string> words1 = new List<string>{...};
            var wordsWithLengthOf8 = words1.Where(w => w.Length == 8).ToList();
            IDictionary<string,string> wordsWithLengthOf8Dic = wordsWithLengthOf8.ToDictionary(w => w);
            IList<string> words2 = new List<string>{...};
            IList<string> matches = new List<string>();   

            for (int i = 0; i < wordsWithLengthOf8.Count; i++)
            {
                var tupled = wordsWithLengthOf8[i].ConcatCheck();
                var isMatch = wordsWithLengthOf8Dic.ContainsKey(tupled.Item1) && wordsWithLengthOf8Dic.ContainsKey(tupled.Item2);
                if (isMatch)
                {
                    matches.Add(wordsWithLengthOf8[i]);
                }
            }

【讨论】:

    猜你喜欢
    • 2020-05-09
    • 1970-01-01
    • 1970-01-01
    • 2021-10-22
    • 2011-09-19
    • 2020-09-03
    • 2011-10-06
    • 1970-01-01
    • 2018-07-18
    相关资源
    最近更新 更多