2011-01-08 3 views
0

私は、渡された量に応じてランダムな提出物を取り戻す機能を作りましたが、大量のデータが通過すると少量のデータでも機能しますが、効率的で問題を引き起こす。LINQ結果のセットをランダム化するより効率的な方法はありますか?

次のような効果的な方法がありますか?

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の小切手が好きではないと思っています。そして、私の頭は今、上記の解決策を見出そうとしています。

答えて

6

(。考慮すべき効率の異なる側面があるとして、を介してすべての方法をお読みください)

は、これを行うのは間違いなくシンプルな方法があります - 特に、あなたは本当に、クエリを実行する必要はありません正しい答えが繰り返される。なぜループ内にrandSubmissionsを取り込んでいますか?またElementAtを見て、SkipFirstOrDefaultを避けてください。randSubmissionsはリストなので、Countプロパティとインデクサーのような通常のリスト操作を使うことができます。

最初に気になるオプションは、部分シャッフルを実行することです。 modified Fisher-Yates shuffleのスタックオーバーフローに関する多くの例があります。そのコードを非常に簡単に変更して、リスト全体をシャッフルするのを避けることができます。必要なだけランダムな要素が得られるまでシャッフルしてください。実際には、これらの日私はおそらくあなただけで呼び出すことができますに少し異なることシャッフルを実装したい:例えば

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

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. 
    } 
} 

上記の方法を考えると、次のようになり、あなたのGetRandomWinners方法:

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(); 
} 

あなたのメソッドにはRandomの新しいインスタンスを作成しないことをお勧めします。私はarticle on preferred ways of using Randomがあります。あなたが検討する必要があります

1つの選択肢は、「行ID」のランダムな選択を計算した後、繰り返しElementAtを使用してエントリを受賞うまく、その後、それをすべて取得することなく、回数正しいエントリのを働いている(と一貫性のある注文)。代わりに、完全な提出を引っ張る代わりに、自分のIDだけを引っ張ってください。あなたはList<T>に入れた(n個のランダムなものを選ぶためにIDをシャッフルし、のようなものを使用します。私はどのように多くのように制限がありますが、これは、SQLで「IN」句を使用すると信じて

return DbContext().Submissions 
        .Where(s => winningIds.Contains(s.Id)) 
        .ToList(); 

をあなたが100,000正しいエントリと3人の勝者を持っている方法も、あなただけ10万のIDが、3つの完全なレコードをフェッチよエントリ次のように取得することができます。

ホープ理にかなっている!

関連する問題