2010-10-30 48 views
2

我试图洗牌一个数组,但我这样做的方式只能每隔五次运行一次。如果有人能解释为什么它不能正常工作,并且可能会提出一个调整,我将不胜感激。无效的阵列洗牌器

private Button[] scrambleBoard(Button[] buttons) 
{ 
    for (int x = 100 * buttons.Count(); x > 0; x--) 
    { 
     Random rand = new Random(); 
     int first = rand.Next(buttons.Count()); 
     int second = rand.Next(buttons.Count()); 


     Button temp = buttons[first]; 
     buttons[first] = buttons[second]; 
     buttons[second] = temp; 
    } 

    return buttons; 
} 

回答

3

移动以下行外循环:

Random rand = new Random(); 

通过System.Random使用的默认种子是基于Environment.TickCount。在紧密的循环中,连续迭代之间的滴答计数可能不会改变,所以它可能最终会一遍又一遍地使用相同的种子。因此,循环将重复交换两个元素相同的,直到标记计数改变(在循环完成之前它可能不这样做)。要验证这是问题,您可以尝试在循环内添加一个Thread.Sleep(100)或类似内容;那么你应该能够看到洗牌工作正常(尽管非常缓慢)。

您还应该注意,technique you're using排列的阵列是有偏见;不是每个排列都是相同的可能性。您可能想要使用已知无偏的混排算法,如Fisher-Yates shuffle

另外,你可以使用一个非常简单的技术来洗牌。它的效率稍低,但没有偏差:

var rand = new Random(); 
return buttons.OrderBy(button => rand.Next()).ToArray(); 
+3

当,我感到哑巴。我再次编码时不需要喝酒......谢谢! – PFranchise 2010-10-30 03:56:03

+1

这是一个很好的白皮书,它解释了有偏差洗牌和PRNG初始化初始化的后果:我们如何学习在线扑克作弊。 http://www.cigital.com/papers/download/developer_gambling.php – PleaseStand 2010-10-30 04:28:25

+0

@idealmachine:很好的一个。 – Ani 2010-10-30 04:34:01

1

问题是你在循环的每次迭代中创建Random()对象。随着Random对象在初始化过程中使用种子,您会发现大多数值将是相同的而不是随机的。

您可以通过在方法主体外声明Random类为静态来解决问题。

private static Random rand = new Random(); 

private Button[] scrambleBoard(Button[] buttons) 
{ 
    for (int x = 100 * buttons.Count(); x > 0; x--) 
    { 
     int first = rand.Next(buttons.Count()); 
     int second = rand.Next(buttons.Count()); 


     Button temp = buttons[first]; 
     buttons[first] = buttons[second]; 
     buttons[second] = temp; 
    } 

    return buttons; 
} 
+0

欣赏响应。 +1! – PFranchise 2010-10-30 03:57:10

+1

随机确实不需要是静态的,顺便说一下...... – 2010-10-30 04:01:43

+1

<随机确实不需要是静态的,顺便说一下>因为它不是线程安全的,所以如果有任何机会多倍线程可能会使用它,它*必须*不是静态的(或者您必须添加适当的同步)。 – 2010-10-30 04:05:17

0

您的问题已被回答,但我想我会分享一个漂亮的小技巧,用LINQ和Guid来洗牌。这创建了一个随机排序的值列表。

private Button[] scrambleBoard(Button[] buttons) 
{ 
    return buttons.OrderBy(b => Guid.NewGuid()).ToArray(); 
}