1

I need to select random objects from a list of horses. At the moment I'm trying to use LINQ. What I need to have happen is the user selects how many object they want and that number of object is randomly selected from the list.

Here is my code at the moment:

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

My question is this a good way of doing this or is an even better way.

6
  • 1
    it's seems to be good enough. it return a random list of horses? Commented May 29, 2014 at 15:25
  • "or an even better way"... seems like you're asking for an opinion? I'm not sure the community will be very receptive to that but I could be wrong. There are many different ways to do this. If this works and makes sense to you, keep it as is. Commented May 29, 2014 at 15:25
  • Is the list of horses coming from a database? Commented May 29, 2014 at 15:29
  • Also, it may be useful for us to see what properties are available on Horse Commented May 29, 2014 at 15:31
  • Define "better." If this works and is fast enough, then why try to improve it? Commented May 29, 2014 at 15:31

3 Answers 3

4

There are two general approaches that you can use when you want to get "N random items from a collection".

  1. Choose a random item, check if it hasn't been chosen before, if it has been, choose a new one.
  2. Shuffle the entire collection, then grab the first N items.

You choose the second option (although you used an inefficient and biased method of doing so).

Which is best, well, that depends. First off, technically, the first option is O(infinity) because there is the possibility of randomly selecting the same item over and over and over again. If you want to speak of average cases, it becomes a valid option to consider.

The first option is best when the number of items you want is a small percentage of the total size of the collection. If you only want to grab say 1% of the collection's items then the odds of randomly choosing already chosen items is very, very small.

The second option, shuffling, on the other hand, is better if you want a large percentage of the items in the collection, because you do the same amount of work regardless of whether you want 1 item or all of them. If you only want 1% of the items in the collection then you end up wasting a whole bunch of effort shuffling data that you just don't care about at all.

Now, what we'd really want to do is randomly shuffle the first N items of the collection, but without shuffling all of the items that are left over.

Interestingly enough, this is actually very easy to do. A much more preferable method of shuffling a list is to count down from the highest index to the lowest, pick an item below the current index, and swap it with the current index. This doesn't introduce a bias, unlike sorting, it's O(n) unlike sorting which is O(n*log(n)), and better still, it's super easy to stop part way. So if we implement this shuffling algorithm:

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

We can now write out your solution as:

var selectedHorses = unasignedHorses.Shuffle(rand).Take(numberHorses);
Sign up to request clarification or add additional context in comments.

1 Comment

Also known as the Knuth Shuffle very nice.
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);

Like I said in the comments though, if your way works now and it makes sense to you, then why change it? I haven't tried this code but I would think something like this would accomplish what you're looking for. It may seem overly complex but that's how my solutions seems to start and then I end up streamlining them...

3 Comments

This option is advantageous if and only if you are selecting a very small percentage of the items in the collection. (This problem is exacerbated by the fact that assignedHorses is a List, not a HashSet.) If you have a collection of 10 million and one horses and you want to select ten million of them, then to get that last hourse you have a 1 in ten million and one shot at choosing the one valid horse left. Every time you fail (and each attempt results in a linear search through a 1 million item list) you have to try again.
I see that now that I have read through your answer. Yours was a wonderful explanation.
You can remove the selected item from the list before looping to remove the need to check if it is already assigned (and meaning also that at any time the name "unassignedHorses" actually makes sense. ;-) This is then actually remarkably close to Servy's answer in many ways (though not necessarily in terms of time complexity since I can believe that List.Remove may be O(n).
-2

So you want to pick random object from list

I guess it can be something done like:

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

2 Comments

I think OP wants to be able to select multiple Horse from unnasignedHorses. Perhaps selectedHorse.Add() might be more appropriate?
He wants to pick multiple items, one would assume without duplicates. Your code won't do that without some modifications.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.