【问题标题】:Why does shuffling two different decks yield the same result? [duplicate]为什么洗牌两个不同的牌组会产生相同的结果? [复制]
【发布时间】:2014-09-12 22:55:26
【问题描述】:

首先我在这里有我的“甲板”课程。我刚刚放入了一些基本方法,用于测试目的。这是我第一次尝试创建“卡片”程序。

    public class Deck
{
    private int deckCounter = 0;
    private List<Card> deckSize = new List<Card>();
    private List<Card> shuffledDeck = new List<Card>();
    private Random random = new Random();

    public Deck()
    {
    }

    public void Build()
    {
        for (int i = 1; i < 5; i++)
        {
            for (int k = 1; k < 14; k++)
            {
                deckSize.Add(new Card(k.ToString(), i));
            }
        }
    }

    public void Add(Card card)
    {
        deckSize.Add(card);
        deckCounter++;
    }

    public Card RemoveCard()
    {
            Card cardToRemove = deckSize.First();
            deckSize.RemoveAt(0);
            return cardToRemove;
    }

    public void ShowContainedCards()
    {
        int cardCount = 0;
        foreach (Card c in deckSize)
        {
            Console.WriteLine(c.ReturnCardInfo());
            cardCount++;
        }
        Console.WriteLine(cardCount);
    }

    public void Shuffle()
    {
        while (deckSize.Count != 0)
        {
            int i = random.Next(deckSize.Count);
            shuffledDeck.Add(deckSize[i]);

            deckSize.RemoveAt(i);
        }
        deckSize = shuffledDeck;
    }

    public bool IsEmpty()
    {
        if (deckSize.Any())
        {
            return false;
        }
        else return true;
    }

    public List<Card> GetCardList()
    {
        return deckSize;
    }
}

基本上,我所做的是:

    Deck deck1 = new Deck();
    Deck deck2 = new Deck();

    deck1.Build();
    deck1.Shuffle();
    deck2.Build();
    deck2.Shuffle();

在那之后,我得到了完全相同的洗牌,甲板 1 和甲板 2。这是为什么?另外,我是这方面的新手,如果你不知道的话:)

【问题讨论】:

  • 它说in the documentation for Random:“通过调用默认构造函数连续创建的不同 Random 对象将具有相同的默认种子值,因此将产生相同的随机数集。”
  • 附带说明,请参阅Fisher-Yates shuffle,这是一种正确的随机播放算法。
  • 已关闭,因为根本问题是相同的,并且现有问题很好地解释了它。
  • 您的 Shuffle 例程也不是很安全 - 我会将 shuffledDeck 设为局部变量而不是字段,并始终在 Shuffle 中使用新的 List&lt;&gt; 对其进行初始化。目前,您不能以理智的方式调用两次Shuffle,因为在第一次调用之后,deckSizeshuffledDeck 引用的是同一个列表。

标签: c# random shuffle


【解决方案1】:

在两个卡片组实例中使用相同的 Random 类实例:

Random random = new Random();
Deck deck1 = new Deck(random);
Deck deck2 = new Deck(random);

所以,在构造函数中:

public class Deck
{
    private int deckCounter = 0;
    private List<Card> deckSize = new List<Card>();
    private List<Card> shuffledDeck = new List<Card>();
    private Random random;

    public Deck(Random random)
    {
        this.random = random;
    }

您的代码的当前问题是创建的两个Random 实例的种子相同。这导致它们产生相同的结果。使用Random 的相同实例意味着第二次洗牌将第一次洗牌的种子结果之上构建。

【讨论】:

  • 这解决了我的问题!也非常感谢您的解释,非常感谢!
【解决方案2】:

来自docs

默认种子值来自系统时钟并且具有有限的分辨率。因此,通过调用默认构造函数连续创建的不同 Random 对象将具有相同的默认种子值,因此将产生相同的随机数集。

相同的文档也提出了解决方案:

可以通过使用单个 Random 对象生成所有随机数来避免这个问题。

因此,一种可能的解决方案是将随机生成器设为静态,以便所有 Deck 实例共享相同的 Random 实例:

private static Random random = new Random();

这样,您甚至可以避免更改 Deck 类的公共接口的任何部分。

【讨论】:

    【解决方案3】:

    计算机本质上是随机的,因此任何随机数生成器实际上都会使用一种算法来生成看起来随机的输出。问题是总有一个起点,如果你知道它从哪里开始,你就可以预测结果。因此,随机数生成器有一个“种子”,它告诉它从哪里开始。相同的种子总是会给出相同的“随机”数字序列。

    这两次,您都在使用new Random(),它使用默认种子。在某些语言中,建议您将当前时间作为种子传递,但 C# 会为您做到这一点。但是,如果您创建两个靠近在一起的 Random 对象,它们很可能获得相同的时间,这就是这里发生的情况。

    如果您将Random 设为静态,那么您的所有随机数都将来自同一个来源,因此两个牌组将获得连续的随机数,而不是并行的相同随机数。

    【讨论】:

      猜你喜欢
      • 2022-01-26
      • 2014-02-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多