【问题标题】:How to shuffle a List<T> [duplicate]如何洗牌 List<T> [重复]
【发布时间】:2009-08-11 19:44:13
【问题描述】:

我在 Wikipedia 找到了这段 Java 代码,它应该对数组进行适当的洗牌:

public static void shuffle (int[] array)
{
    Random rng = new Random();
    int n = array.length;
    while (n > 1) 
    {
        n--;  
        int k = rng.nextInt(n + 1);
        int tmp = array[k];
        array[k] = array[n];
        array[n] = tmp;
    } 
}

虽然我没有测试代码,但看起来它应该可以很好地处理数组。在我的 C# 项目中,我创建了一个 CardSet 类并在 Shuffle() 方法中使用了上面的代码:

public class CardSet
{
    private List<Card> cards;

    public Card this[int i]
    {
        get {  return cards[i];  }
        set {  cards[i] = value;  }
    }

    public void Shuffle()
    {
        Random rng = new Random();
        int n = this.NumberOfCards;

        while (n < 1)
        {
            n--;
            int k = rng.Next(n + 1);
            Card tmp = this[k];
            this[k] = this[n];
            this[n] = tmp;
        }
    }

但是,当我使用该方法时,不会发生改组:

CardSet cs = new CardSet();
cs.Shuffle(); 

foreach (Card c in cs)
{
    Console.WriteLine(c.ToString());
}    

我只是想不通为什么它不起作用。我认为 List 可能会自动对其对象进行排序,因此我尝试更改其值之一,

cs[8] = new Card(Suites.Hearts, Numbers.Two);

Card 正是我放的地方。要么我犯了一些简单的错误,要么我没有正确编写洗牌算法。如果我提供的代码看起来不错,并且有人认为错误可能在我的代码中的其他地方,我可以提供我的其余代码。

【问题讨论】:

  • 1) 这个洗牌功能很差。 2)你为什么要解除这么简单的方法而不是自己写?
  • @Spencer - 即使您可以轻松推出自己的解决方案,检查其他人对常见问题的解决方案总是值得的。你可能只是学到了一些东西。诀窍是能够区分好的和坏的实现。
  • 我想先让一个简单的函数工作。接下来我想我会尝试使用 RNGCrypoServiceProvider 或 Fredou 链接中的 GUID
  • @Spencer:洗牌一点也不差,它实际上是洗牌现有列表的最佳方法。它保证每张牌都有相同的机会出现在牌组中的每个位置,并且它用尽可能少的工作量来做到这一点。改进算法的唯一方法是使用具有更好随机性的随机生成器。如果你想要一个好的算法,问问你自己:WWKD (What Will Knuth Do)。
  • 这看起来是一个 Knuth-Fisher-Yate 洗牌,没有严重的问题(假设 PRNG 不错,这完全是另一回事)。请参阅 Jeff Atwood 对此的启示:codinghorror.com/blog/archives/001015.html

标签: c# .net generics


【解决方案1】:

改变

while (n < 1)

while (n > 1)

【讨论】:

  • 我应该抓住它!谢谢
【解决方案2】:

【讨论】:

  • 酷,我喜欢 :D,希望生成 GUID 在性能方面不会很痛苦,但我认为没关系
  • 这类似于你(我)在 (MS)SQL 中对列表进行洗牌(不确定其他风格)SELECT * FROM Table ORDER BY newid()
  • 使用排序来shuffle不如问题中的算法。随机值的范围是有限的,这意味着两张牌有可能会得到相同的随机值,在这种情况下它们不会被随机排序。
  • GUID 是唯一的,不是随机的。 blogs.msdn.com/b/oldnewthing/archive/2012/05/23/10309199.aspx
  • @MarkSowul,该声明与该问题/解决方案有什么相关性?如果您正在评论 Guffa 评论,请使用 (at)Guffa 指定它,并且不要对其有效的解决方案投反对票。
【解决方案3】:

您的 while 循环已关闭。它说当 n 小于 1。你将 n 设置为卡片的数量。所以说如果你有 52 张卡片,n 肯定大于 1 并且你的循环不会执行。

所以将你的 while 循环更改为如下所示:

while(n > 1)

【讨论】:

    【解决方案4】:

    您的示例方法中的代码和您的应用程序中的代码(第一个和第二个块)是不同的。在一个中,您使用

    While (n < 1)
    

    在你使用的另一个中

    While (n > 1)
    

    在您的示例中使用正确的一个 - “(n > 1)”。如果你使用另一个,你的循环甚至不会执行一次——它会跳过条件并且你的牌组保持不变。

    也就是说,如果您可以使用 LINQ,那么它是一个更好的选择。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-04-13
      • 1970-01-01
      • 2017-01-26
      • 1970-01-01
      • 1970-01-01
      • 2013-02-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多