是否有更有效的方法来随机化一组LINQ结果?
我已经生成了一个函数来取回一组随机的提交,具体取决于传递给它的数量,但是我担心即使现在通过少量数据传输大量数据,它也会变得很有效率。引起问题。
是否有更有效的方式来做以下事情?
public List GetRandomWinners(int id) { List submissions = new List(); int amount = (DbContext().Competitions .Where(s => s.CompetitionId == id).FirstOrDefault()).NumberWinners; for (int i = 1 ; i s.CompetitionId == id && s.CorrectAnswer).ToList(); int count = randSubmissions.Count(); int index = new Random().Next(count); foreach (var sub in submissions) { if (sub == randSubmissions.Skip(index).FirstOrDefault()) found = true; } if (!found) { submissions.Add(randSubmissions.Skip(index).FirstOrDefault()); added = true; } } } return submissions; }
正如我所说,我已经完全正常工作并带回了想要的结果。 只是我不喜欢foreach
而且在那里检查时我的脑袋刚刚变成糊状,现在试图想出上述解决方案。
(请仔细阅读,因为要考虑效率的不同方面。)
肯定有更简单的方法 – 特别是,您实际上不需要重复执行查询以获得正确的答案。 你为什么要在循环中获取randSubmissions
? 您还应该查看ElementAt
以避免Skip
和FirstOrDefault
– 并且请记住,由于randSubmissions
是一个列表,您可以使用常规列表操作,如Count
属性和索引器!
首先想到的选项是执行部分随机播放。 在改进的Fisher-Yates shuffle的 Stack Overflow上有大量示例。 您可以非常轻松地修改该代码,以避免改变整个列表 – 只需将其随机播放,直到您拥有所需数量的随机元素为止。 事实上,这些天我可能会稍微改变一下,你可以调用:
return correctSubmissions.Shuffle(random).Take(amount).ToList();
例如:
public static IEnumerable Shuffle (this IEnumerable source, Random rng) { T[] elements = source.ToArray(); for (int i = 0; i < elements.Length; i++) { // Find an item we haven't returned yet int swapIndex = i + rng.Next(elements.Length - i); T tmp = elements[i]; yield return elements[swapIndex]; elements[swapIndex] = tmp; // Note that we don't need to copy the value into elements[i], // as we'll never use that value again. } }
鉴于上述方法,您的GetRandomWinners方法将如下所示:
public List GetRandomWinners(int competitionId, Random rng) { List submissions = new List (); int winnerCount = DbContext().Competitions .Single(s => s.CompetitionId == competitionId) .NumberWinners; var correctEntries = DbContext().Submissions .Where(s => s.CompetitionId == id && s.CorrectAnswer) .ToList(); return correctEntries.Shuffle(rng).Take(winnerCount).ToList(); }
我建议不要在你的方法中创建一个新的Random
实例。 我有一篇关于使用Random
首选方法的文章 ,你会发现它很有用。
您可能需要考虑的一个替代方案是计算正确条目的计数而不将其全部取出,然后通过计算随机选择的“行ID”然后重复使用ElementAt
(具有一致的顺序)来计算获胜条目。 或者,不要拉动完整的提交,而只需提取他们的ID。 随机播放ID以随机选择(您将其放入List
,然后使用以下内容:
return DbContext().Submissions .Where(s => winningIds.Contains(s.Id)) .ToList();
我相信这将在SQL中使用“IN”子句,尽管可以像这样检索多少条目存在限制。
这样,即使您有100,000个正确的条目和3个获胜者,您也只能获取100,000个ID,但有3个完整记录。 希望有道理!