我列出了10种方法。现在我想以随机顺序调用这些方法。序列应该在运行时生成。这是最好的方法吗?
答案 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();
}
答案 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)
我想:
通过反射获取方法对象;
创建一个创建的方法对象数组;
生成随机索引(规范化范围);
调用方法;
您可以从数组中删除方法以执行一次方法。
再见