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

3 回答 3

3

将以下行移到循环外:

Random rand = new Random();

使用的默认种子System.Random基于Environment.TickCount. 在紧密循环中,滴答计数可能不会在连续迭代之间发生变化,因此它可能会一遍又一遍地使用相同的种子。因此,循环将重复交换相同的两个元素,直到滴答计数发生变化(在循环完成之前它可能不会这样做)。要验证这是问题所在,您可以尝试Thread.Sleep(100) 在循环中添加一个或类似的;然后,您应该能够看到 shuffle 正常工作(尽管速度很慢)。

您还应该注意,用来置换数组的技术是有偏差的;并非每个排列都有同样的可能性。您可能想要使用已知无偏的洗牌算法,例如Fisher-Yates shuffle

或者,您可以使用一种非常简单的技术来洗牌。它的效率略低,但不偏不倚:

var rand = new Random();
return buttons.OrderBy(button => rand.Next()).ToArray();
于 2010-10-30T03:54:46.530 回答
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;
}
于 2010-10-30T03:55:01.237 回答
0

您的问题已得到解答,但我想我会分享一个使用 LINQ 和 Guid 洗牌集合的好小技巧。这会创建一个随机排序的列表,其中包含很好的值分布。

private Button[] scrambleBoard(Button[] buttons)
{
    return buttons.OrderBy(b => Guid.NewGuid()).ToArray();
}
于 2010-10-30T09:49:14.410 回答