是否有更有效的方法来随机化一组LINQ结果?

时间:2011-01-08 10:20:08

标签: c# linq random

我已经生成了一个函数来取回一组随机的提交,具体取决于传递给它的数量,但是我担心即使现在通过少量数据传递大量数据,它也会变得有效并导致问题。

是否有更有效的方法来做以下事情?

    public List<Submission> GetRandomWinners(int id)
    {
        List<Submission> submissions = new List<Submission>();
        int amount = (DbContext().Competitions
                     .Where(s => s.CompetitionId == id).FirstOrDefault()).NumberWinners;

        for (int i = 1 ; i <= amount; i++)
        {
            bool added = false;
            while (!added)
            {
                bool found = false;

                var randSubmissions = DbContext().Submissions
                    .Where(s => 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;
    }

正如我所说,我已经完全正常工作并带回了想要的结果。只是因为我不喜欢那里的foreachwhile支票而且我的头脑刚刚变成糊状,现在试图提出上述解决方案。

1 个答案:

答案 0 :(得分:6)

(请仔细阅读,因为要考虑效率的不同方面。)

确实有更简单的方法 - 特别是,您实际上不需要重复执行查询以获得正确的答案。为什么要在循环中获取randSubmissions?您还应该查看ElementAt以避免SkipFirstOrDefault - 并且请记住,由于randSubmissions是一个列表,您可以使用常规列表操作,例如{ {1}}属性和索引器!

首先想到的选项是执行部分随机播放。 modified Fisher-Yates shuffle的Stack Overflow上有大量示例。您可以非常轻松地修改该代码,以避免混乱整个列表 - 只需将其随机播放,直到您拥有所需数量的随机元素为止。事实上,这些天我可能会稍微改变一下,你可以调用:

Count

例如:

return correctSubmissions.Shuffle(random).Take(amount).ToList();

鉴于上述方法,您的GetRandomWinners方法将如下所示:

public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> 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.
    }
}

我建议您不要在方法中创建public List<Submission> GetRandomWinners(int competitionId, Random rng) { List<Submission> submissions = new List<Submission>(); 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(); } 的新实例。我有article on preferred ways of using Random你可能会觉得有用。

您可能需要考虑的另一个选择是计算出正确条目的 count 而不是全部取出它们,然后通过计算随机选择的“行ID”然后使用{来计算获胜条目重复{1}}(顺序一致)。或者,不要拉动完整的提交,而只需提取他们的ID。随机抽取ID以随机选择(您放入Random,然后使用以下内容:

ElementAt

我相信这会在SQL中使用“IN”子句,尽管可以像这样检索多少条目有限制。

即使您有100,000个正确的条目和3个获胜者,您也只能获取100,000个ID,但会获得3个完整记录。希望有意义!