Calling a list of methods in random order?

I have a list of 10 methods. Now I want to call these methods in random order. The sequence must be generated at run time. What is the best way to do this?

+4
source share
7 answers

I am always struck by the number of incorrect and ineffective answers that everyone sees when someone asks how to shuffle the list of things in StackOverflow. Here we have some examples of code that is fragile (because it assumes that key collisions are impossible, because they are actually just rare) or slow for large lists. (In this case, it is believed that the problem consists of only ten elements, but when possible, it is better to give a solution that scales to thousands of elements if it is not difficult.)

This is not difficult to solve. The right, quick way to do this is to create an array of actions and then shuffle this array in place using the Fisher-Yates Shuffle.

http://en.wikipedia.org/wiki/Fisher-Yates_shuffle

Some things you cannot do:

  • Do not use Fischer-Yates in a random manner. More errors are seen than the correct implementations of this trivial algorithm. In particular, make sure you select a random number from the correct range. Choosing from the wrong range results in a biased shuffle.

  • If the shuffle algorithm should be unpredictable, use a random source other than Random, which is only pseudo-random. Remember that Random has only 2 32 possible seeds, and therefore there are fewer than possible.

  • If you are going to make a lot of shuffling in a short period of time, do not create a new instance of Random every time. Save and reuse the old one or use another source of randomness. Random picks his seed based on time; many Randoms created in close sequence will generate the same sequence of "random" numbers.

  • Do not sort the "random" GUID as a key. GUIDs are guaranteed to be unique. They are not guaranteed to be random. For implementation it is quite logical to spit out consecutive GUIDs.

  • Do not use a random function as a comparator or pass it to a sorting algorithm . Sorting algorithms allow you to do anything if the comparator is bad, including a failure, including producing nonrandom results. As Microsoft recently discovered, it is extremely difficult to get a simple algorithm like this.

  • Do not use random input as a key for a dictionary, and then sort the dictionary. There is nothing that would prevent the source of randomness from choosing the same key twice and, therefore, either crashing your application with a duplicate key exception or silently losing one of your methods.

  • Do not use the Create Two Lists algorithm. Add items to the first list. Repeatedly move a random item from the first list to the second list, removing the item from the first list. "If the list is O (n) to remove the item, then this is O (n 2 ) algorithm.

  • Do not use the Create Two Lists algorithm. Add items to the first list. Repeatedly move a random non-zero element from the first list to the second list, setting the element in the first list to null. " Also do not make this crazy equivalent of this algorithm. If there are many elements in the list, it gets slower and slower when you start to type more and more zeros.

+15
source

New short answer

Starting from the moment when Ilya Kogan ceased to exist, it’s completely correct after Eric Lippert found the error:

var methods = new Action[10]; var rng = new Random(); var shuffled = methods.Select(m => Tuple.Create(rng.Next(), m)) .OrderBy(t => t.Item1).Select(t => t.Item2); foreach (var action in shuffled) { action(); } 

Of course, this does a lot behind the scenes. The method below should be much faster. But if LINQ is fast enough ...

Old answer (much longer)

After stealing this code from here :

 public static T[] RandomPermutation<T>(T[] array) { T[] retArray = new T[array.Length]; array.CopyTo(retArray, 0); Random random = new Random(); for (int i = 0; i < array.Length; i += 1) { int swapIndex = random.Next(i, array.Length); if (swapIndex != i) { T temp = retArray[i]; retArray[i] = retArray[swapIndex]; retArray[swapIndex] = temp; } } return retArray; } 

the rest is easy:

 var methods = new Action[10]; var perm = RandomPermutation(methods); foreach (var method in perm) { // call the method } 
+5
source

Have an array of delegates. Suppose you have this:

 class YourClass { public int YourFunction1(int x) { } public int YourFunction2(int x) { } public int YourFunction3(int x) { } } 

Now declare the delegate:

 public delegate int MyDelegate(int x); 

Now create an array of delegates:

 MyDelegate delegates[] = new MyDelegate[10]; delegates[0] = new MyDelegate(YourClass.YourFunction1); delegates[1] = new MyDelegate(YourClass.YourFunction2); delegates[2] = new MyDelegate(YourClass.YourFunction3); 

and now call it like this:

 int result = delegates[randomIndex] (48); 
+2
source

You can create a shuffled collection of delegates and then call all the methods in the collection.

Here is an easy way to do this with a dictionary. The dictionary keys are random numbers, and the values ​​are delegates to your methods. When you iterate through a dictionary, it has the effect of shuffling.

 var shuffledActions = actions.ToDictionary( action => random.Next(), action => action); foreach (var pair in shuffledActions.OrderBy(item => item.Key)) { pair.Value(); } 
  • actions are an enumeration of your methods.
  • random is of type Random.
+2
source

When processing a list in random order, the natural tilt is to shuffle the list.

Another approach is to simply preserve the order of the list, but randomly select and delete each item.

 var actionList = new[] { new Action( () => CallMethodOne() ), new Action( () => CallMethodTwo() ), new Action( () => CallMethodThree() ) }.ToList(); var r = new Random(); while(actionList.Count() > 0) { var index = r.Next(actionList.Count()); var action = actionList[index]; actionList.RemoveAt(index); action(); } 
+1
source

Think of it as a list of objects, and you want them to randomly retrieve objects. You can get a random index using the Random.Next Method (always use the current List.Count parameter as) and then remove the object from the list so that it will no longer be drawn.

+1
source

I think:

  • Via objects get get Method objects

  • create an array of the created method object;

  • generate a random index (normalize the range);

  • call method;

You can remove a method from an array to execute the method once.

Bye

0
source

Source: https://habr.com/ru/post/1346317/


All Articles