2014-05-29 2 views
1

Мне нужно выбрать случайные объекты из списка лошадей. На данный момент я пытаюсь использовать LINQ. Что мне нужно, так это то, что пользователь выбирает, сколько объектов они хотят и что номер объекта случайно выбран из списка.Выбор нескольких объектов из списка с помощью LINQ

Вот мой код на данный момент:

Random rand = new Random(); 

//creates a list of horses that haven't yet been assigned to players 
List<Horse> unassignedHorses = retrieveUnassignedHorses(); 

List<Horse> selectedHorses = unassignedHorses.OrderBy(m => rand.Next()) 
    .Take(numberHorses) 
    .ToList(); 

Мой вопрос это хороший способ сделать это или еще лучше.

+1

это, кажется, достаточно хорошо. он возвращает случайный список лошадей? – BlaShadow

+0

«или еще лучший способ» ... похоже, вы просите мнение? Я не уверен, что сообщество будет очень восприимчиво к этому, но я могу ошибаться. Существует много разных способов сделать это. Если это сработает и имеет смысл для вас, сохраните его как есть. – Jfabs

+0

Является ли список лошадей, поступающих из базы данных? – DavidG

ответ

-2

Итак, вы хотите, чтобы выбрать случайный объект из списка

Я предполагаю, что это может быть что-то сделано, как:

int r = rnd.Next(numberHorses.Count); 
Horse selectedHorse = unnasignedHorses[r]; 
+0

Я думаю, что OP хочет иметь возможность выбирать несколько «Лошадей» из «untasignedHorses». Возможно, 'selectedHorse.Add()' может быть более уместным? – Jfabs

+0

Он хочет выбрать несколько предметов, которые можно было бы использовать без дубликатов. Ваш код не будет делать этого без каких-либо изменений. –

+0

Его код уже работает, ваш выбирает только один объект. – DavidG

4

Есть два основных подхода, которые можно использовать, когда вы хотите получить «N случайных предметы из коллекции ".

  1. Выберите случайный элемент, убедитесь, что он не был выбран раньше, если он был, выберите новый.
  2. Смешайте всю коллекцию, затем возьмите первые N предметов.

Вы выбираете второй вариант (хотя вы использовали неэффективный и предвзятый способ сделать это).

Что лучше, ну, это зависит. Во-первых, технически первый вариант - O (бесконечность), потому что существует возможность случайного выбора одного и того же элемента снова и снова. Если вы хотите говорить о средних случаях, это станет правильным вариантом для рассмотрения.

Первый вариант лучше всего, когда количество элементов, которые вы хотите, представляет собой небольшой процент от общего размера коллекции. Если вы хотите захватить только 1% предметов коллекции, то шансы случайного выбора уже выбранных предметов очень и очень малы.

Второй вариант, перетасовка, с другой стороны, лучше, если вы хотите, чтобы процентные элементы в коллекции составляли , потому что вы выполняете ту же работу независимо от того, хотите ли вы 1 элемент или все их. Если вы хотите только 1% предметов в коллекции, вы в конечном итоге тратите массу усилий на перетасовку данных, которые вам просто не нравятся.

Теперь, что мы должны были действительно хотите сделать случайным образом перетасовать первые N элементов коллекции, но без перетасовки всех предметов, которые остались.

Интересно, что на самом деле это очень легко сделать. Гораздо более предпочтительный метод перетасовки списка - это отсчет с самого высокого индекса до самого низкого, выберите элемент под текущим индексом и замените его текущим индексом. Это не приводит к смещению, в отличие от сортировки, это O (n) в отличие от сортировки, которая является O (n * log (n)), и, что еще лучше, это супер легко остановить путь части.Так что, если мы реализуем этот перетасовки алгоритм:

public static IEnumerable<T> Shuffle<T>(
    this IEnumerable<T> source, Random random) 
{ 
    var list = source.ToList(); 
    for (int i = list.Count; i > 0; i--) 
    { 
     var index = random.Next(i); 
     var temp = list[index]; 
     list[index] = list[i - 1]; 
     list[i - 1] = temp; 
     yield return temp; 
    } 
} 

Теперь мы можем выписать свое решение, как:

var selectedHorses = unasignedHorses.Shuffle(rand).Take(numberHorses); 
+0

Также известен как [Knuth Shuffle] (http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle) очень приятно. –

0
int numberHorses = 3; //or some user input 
Random r = new Random(); 
List<Horse> unassignedHorses = retriveUnassignedHorses(); 
List<Horse> assignedHorses = new List<Horse>(); 
do 
{ 
    int rIint = r.Next(0, unnasignedHorses.Count); 
    if(!assignedHorses.Contains(unassignedHorses[rInt])) 
    { 
    assignedHorses.Add(unassignedHorses[rInt]); 
    } 
} (while assignedHorses.Count != numberHorses); 

Как я уже говорил в комментариях, хотя, если ваш путь в настоящее время работает, и это делает смысл для вас, то зачем это менять? Я не пробовал этот код, но я бы подумал, что что-то подобное выполнит то, что вы ищете. Это может показаться чересчур сложным, но вот как мои решения, похоже, начинаются, а затем я в конечном итоге их упорядочиваю ...

+0

Эта опция выгодна тогда и только тогда, когда вы выбираете очень небольшой процент элементов в коллекции. (Эта проблема усугубляется тем фактом, что 'assignHorses' является' List', а не 'HashSet'.) Если у вас есть коллекция из 10 миллионов и одна лошадь, и вы хотите выбрать десять миллионов из них, то для получения этого последний час, у вас есть 1 на десять миллионов и один выстрел при выборе одной действующей лошади. Каждый раз, когда вы терпите неудачу (и каждая попытка приводит к линейному поиску через список из 1 миллиона элементов), вы должны попробовать еще раз. – Servy

+0

Я вижу, что теперь, когда я прочитал ваш ответ. Твое было прекрасным объяснением. – Jfabs

+0

Вы можете удалить выбранный элемент из списка перед циклом, чтобы удалить необходимость проверки, если он уже назначен (и также означает, что в любое время имя «unassignedHorses» действительно имеет смысл. ;-) Это тогда действительно замечательно близко к Сервисный ответ во многом (хотя и не обязательно с точки зрения временной сложности, так как я могу поверить, что 'List.Remove' может быть O (n). – Chris

Смежные вопросы