以随机顺序调用方法列表?

时间:2011-04-01 22:09:21

标签: c#

我列出了10种方法。现在我想以随机顺序调用这些方法。序列应该在运行时生成。这是最好的方法吗?

7 个答案:

答案 0 :(得分:16)

每当有人问起如何在StackOverflow上混淆事物列表时,我总会惊讶地发现错误和低效答案的数量。这里我们有几个代码很脆弱的例子(因为它假设密钥冲突是不可能的,实际上它们只是罕见的)或大型列表的速度慢。 (在这种情况下,问题只是十个元素,但是如果可能的话,最好提供一个可以扩展到数千个元素的解决方案,如果这样做并不困难。)

这不是一个难以正确解决的问题。正确,快速的方法是创建一个动作数组,然后使用Fisher-Yates Shuffle就地对该数组进行洗牌。

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

有些事要做不到:

  • 不要错误地实施Fischer-Yates shuffle。人们认为这种琐碎算法的正确实现更不正确。特别是,请确保从正确的范围中选择随机数。从错误的范围中选择它会产生有偏差的混乱。

  • 如果shuffle算法实际上必须不可预测,则使用Random之外的随机源,这只是伪随机的。记住,Random只有2个 32 可能的种子,因此可能的shuffles数量少于许多。

  • 如果您要在很短的时间内制作许多shuffle,请不要每次都创建一个新的Random实例。保存并重新使用旧的,或完全使用不同的随机源。随机根据时间选择种子;很多连续创建的Randoms会生成相同的“随机”数字序列。

  • 不要将“随机”GUID排序为您的密钥。 GUID保证唯一。它们不保证随机排序。实现吐出连续的GUID是完全合法的。

  • Do not use a random function as a comparator and feed that to a sorting algorithm。如果比较器坏,包括崩溃,并且包括产生非随机结果,则允许排序算法做任何他们喜欢的事情。作为Microsoft recently found out, it is extremely embarrassing to get a simple algorithm like this wrong.

  • 不要将输入随机用作字典的键,然后对字典进行排序。没有什么可以阻止随机源两次选择相同的密钥,因此要么使用重复的密钥异常导致应用程序崩溃,要么默默地丢失一种方法。

  • 不要使用算法“创建两个列表。将元素添加到第一个列表。重复将随机元素从第一个列表移动到第二个列表,从第一个列表中删除元素”。如果列表是O(n)以删除项目,则这是O(n 2 )算法。

  • 不要使用算法“创建两个列表。将元素添加到第一个列表。重复地将随机非空元素从第一个列表移动到第二个列表,将第一个列表中的元素设置为null “。 Also do not do this crazy equivalent of that algorithm.如果列表中有很多项目,那么当你开始点击越来越多的空值时,这会越来越慢。

答案 1 :(得分:5)

新的简短回答

从Ilya Kogan离开的地方开始,在我们让Eric Lippert找到错误之后完全正确:

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

当然,这在幕后做了很多。下面的方法应该更快。但如果LINQ足够快......

旧答案(更长)

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

其余的很容易:

var methods = new Action[10];
var perm = RandomPermutation(methods);

foreach (var method in perm)
{
    // call the method
}

答案 2 :(得分:2)

拥有一系列代表。假设你有这个:

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

现在宣布一个代表:

public delegate int MyDelegate(int x);

现在创建一个委托数组:

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

现在称之为:

int result = delegates[randomIndex] (48);

答案 3 :(得分:2)

您可以创建一个随机的委托集合,然后调用集合中的所有方法。

使用字典这是一种简单的方法。字典的键是随机数,值是您方法的委托。当你遍历字典时,它具有改组的效果。

var shuffledActions = actions.ToDictionary(
                                action => random.Next(), 
                                action => action);

foreach (var pair in shuffledActions.OrderBy(item => item.Key))
{
    pair.Value();
}
  • 操作是您的方法的可枚举。
  • 随机是Random类型。

答案 4 :(得分:1)

当以随机顺序处理列表时,自然倾向于随机播放列表。

另一种方法是保持列表顺序,但随机选择并删除每个项目。

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

答案 5 :(得分:1)

认为这是一个对象列表,您希望它随机提取对象。您可以使用Random.Next Method获取随机索引(始终使用当前List.Count作为参数),然后从列表中删除对象,以便不再绘制它。

答案 6 :(得分:0)

我想:

  1. 通过反射获取方法对象;

  2. 创建一个创建的方法对象数组;

  3. 生成随机索引(规范化范围);

  4. 调用方法;

  5. 您可以从数组中删除方法以执行一次方法。

    再见