(Пожалуйста, прочитайте все до конца, поскольку есть различные аспекты эффективности, которые следует учитывать.)
Существуют, безусловно, более простые способы сделать это - и, в частности, вам действительно не нужно выполнятьзапрашивать правильные ответы повторно.Почему вы извлекаете randSubmissions
внутри цикла?Вам также следует взглянуть на ElementAt
, чтобы избежать Skip
и FirstOrDefault
- и иметь в виду, что, поскольку randSubmissions
является списком, вы можете использовать обычные операции со списками, такие как свойство Count
и индексатор!
Первый вариант, который приходит на ум, - это частичное перемешивание.Существует множество примеров переполнения стека модифицированного шагала Фишера-Йейтса .Вы можете очень легко изменить этот код, чтобы избежать перемешивания всего списка - просто перемешивайте его, пока не получите столько случайных элементов, сколько вам нужно.Фактически, в наши дни я бы, вероятно, реализовал этот случай случайным образом немного иначе, чем вы могли бы просто вызвать:
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
в вашем методе.У меня есть статья о предпочтительных способах использования Random
, которую вы можете найти полезной.
Одна из альтернатив, которую вы можете рассмотреть, - это вычисление count изисправьте записи, не извлекая их все, затем отработайте выигрышные записи, рассчитав случайный выбор «идентификаторов строк», а затем повторно используйте ElementAt
(с единообразным порядком).В качестве альтернативы, вместо того, чтобы извлекать полные представления, просто извлекайте их идентификаторы.Перемешайте идентификаторы, чтобы выбрать n случайных (которые вы вводите в List<T>
, затем используйте что-то вроде:
return DbContext().Submissions
.Where(s => winningIds.Contains(s.Id))
.ToList();
Я полагаю, что в SQL будет использоваться предложение «IN», хотя существуют ограниченияотносительно того, сколько записей можно извлечь следующим образом.
Таким образом, даже если у вас 100 000 правильных записей и 3 победителя, вы получите только 100 000 идентификаторов и 3 полные записи. Надеюсь, это имеет смысл!