2

How can I shuffle present items in a listBox randomly?

I have this code, but it is very slow and inefficient -

private void shuffleItemsToolStripMenuItem_Click(object sender, EventArgs e)
{
   ListBox.ObjectCollection list = listBox1.Items;
   Random rng = new Random();
   int n = list.Count;
   while ( n > 1 )
   {
      n--;
      int k = rng.Next(n + 1);
      string value = (string)list[k];
      list[k] = list[n];
      list[n] = value;
   }
}

While this code does technically work, it's very slow and doesn't give many variations, often giving the same few shuffles.

I've been searching on Google for the last hour and have been unable to find a solution to this. I am running out of hair to pull.

Thanks.

Brian
  • 5,069
  • 7
  • 37
  • 47
John
  • 2,015
  • 5
  • 23
  • 37
  • On [this post](http://stackoverflow.com/questions/1150646/card-shuffling-in-c-sharp) there are some answers that will help you. One leads you to the Fisher-Yates Shuffle algorithm. – Mike Perrenoud Dec 02 '13 at 21:19
  • I can't see how I can adapt the Fisher-Yates algorithm to a listBox. I need much help with this. – John Dec 02 '13 at 21:33
  • I have used the code you posted without issue, how big is the list you are shuffling and why do you think it does not give many variations? – TimS Dec 02 '13 at 21:47
  • A list of 11+ items. Shuffling the list 10 times yields the same 2 or three list with the same order. I need code that shuffles it randomly and uniquely, and doesn't take 2+ seconds. – John Dec 02 '13 at 22:03
  • 1
    @Smith I can't possibly imagine this algorithm taking several seconds to sort an 11 item list. Nowhere even remotely near that. If that's your problem, it isn't related to the shuffling at all. It must be due to something unrelated to the actual shuffling. As for getting different shuffles, it's possible that you're creating `Random` instances within a close enough timespan that they get the same seed; if not, you must have small enough sequences and enough iterations that the odds of collisions are actually reasonably high. Note the "birthday paradox" is at work in that regard. – Servy Dec 02 '13 at 22:40
  • This code should definitely not take 2 seconds to run for a list of 11 items shuffled 10 times. Are you sure you aren't including compile time in that number? Also it should be shuffled uniquely each time because `new Random()` uses the current time as the seed. If you don't trust that you could always do `rng = new Random(currentTime)` to seed it yourself. – Steven Dec 02 '13 at 22:46
  • I'm sure that it's taking 2 seconds, give or take a few milliseconds. It's absolutely atrocious. – John Dec 02 '13 at 23:23
  • This is being run from the executable or are you recompiling each time? – Steven Dec 02 '13 at 23:25
  • From the executable. It's slow. I've re-written a new method. Thanks for all the help. [/sarcasm] – John Dec 02 '13 at 23:53

2 Answers2

2

I guess you're using winforms, a ListBox has a pair of methods BeginUpdate and EndUpdate used to update the items fast (no need to repaint), repainting when changing the items is one of the reason slowing down everything. Also note that you don't need any cast here, it may slow your code a little, try the following:

private void shuffleItemsToolStripMenuItem_Click(object sender, EventArgs e) {
   ListBox.ObjectCollection list = listBox1.Items;       
   Random rng = new Random();
   int n = list.Count;
   //begin updating
   listBox1.BeginUpdate();
   while ( n > 1 ) {
    n--;
    int k = rng.Next(n + 1);
    object value = list[k];
    list[k] = list[n];
    list[n] = value;
   }
   listBox1.EndUpdate();
   listBox1.Invalidate();
}

However I agree that even without using some tweaks above, your code should not run so slowly.

King King
  • 61,710
  • 16
  • 105
  • 130
0

Try the Fisher-Yates shuffle : http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle

Dan Dinu
  • 32,492
  • 24
  • 78
  • 114