2

我目前正在制作 ac# 版本的内存(游戏),现在我需要制作一种洗牌的方法。我有这样的东西(但它还没有工作):

    public void shuffle()
    {
        for (int i = 0; i < 100000; i++)
        {
            Random k = new Random();
            Random k2 = new Random();

            kaarten[k.Next(0, 11)] = kaarten[k2.Next(0,11)];
            kaarten[k2.Next(0, 11)] = kaarten[k.Next(0, 11)];
        }
    }

所以我想知道是否有人可以帮助我,在此先感谢!史蒂文。

4

5 回答 5

3

考虑kaarten是一个List代表卡片的东西......

public void Shuffle()
{
    // Insert cards at random order into the shuffled list
    var shuffled = new List<Card>();
    var rand = new Random();

    // As long as there are any cards left to insert randomly
    while (kaarten.Count != 0)
    {
        // Get the index of the random card to insert
        var i = rand.Next(kaarten.Count);

        // Insert it
        shuffled.Add(kaarten[i]);

        // Remove from non-shuffled list
        kaarten.RemoveAt(i);
    }

    // Set the list of cards to the shuffled list
    kaarten = shuffled;
}

您当前代码的问题:

您不会将随机数保存到局部变量中,因此当您尝试交换它们时,您实际上有 4 个随机数,而不是 2 个随机数。

此外,为了交换数组中的两个元素,您应该使用 tmp 变量,就像在 Swap 算法中所见的那样,几乎可以在您看到的任何地方看到。

然而,对于一个完整的洗牌,我的方法不需要决定循环交换多少次以获得足够的洗牌,因此更有效也更容易理解。

还有另一种打乱列表的方法,如果您愿意,这有点令人困惑(效率最低)但更短:

var rand = new Random();
kaarten = kaarten.Select(x => new{X=x,R=rand.Next()})
                 .OrderBy(x => x.R)
                 .Select(x => x.X)
                 .ToList();
                 //.ToArray(); if kaarten is an array and not a list
于 2013-06-15T10:06:54.860 回答
2

您的代码的第一个大问题是您正在创建两个Random. 糟糕的播种new Random()意味着这些实例很可能会返回完全相同的序列。

new Random()使用 的种子Environment.TickCount,它每隔几毫秒就会改变一次。因此,如果您Random快速连续创建两个实例,时间将相同,因此它们输出相同的序列。

正确的解决方案是Random在开始时只创建一个实例,并使用 if 来满足您的所有随机性需求。请注意多线程,实例Random不是线程安全的。

另请注意, 的上限random.Next是独占的,因此您的代码仅适用于具有 11 个元素的数组。最好使用集合大小而不是硬编码值。

您的代码的另一个问题是您没有实现正确的交换。要进行交换,您需要进行交换有两个问题:您在第二个方向使用新索引,并且您没有在局部变量中创建临时副本以避免读取覆盖的值,而不是原始值。

修复了这些问题后,您的代码如下所示:

Random random = new Random();//one persistent instance

public void shuffle()
{
    for (int i = 0; i < 100000; i++)
    {
        var i1=random.Next(kaarten.Count);
        var i2=random.Next(kaarten.Count);

        var temp=kaarten[i1];
        karten[i1]=kaarten[i2];
        karten[i2]=temp;
    }
}

也就是说,你的方法有点低效,因为你迭代了 100000 次。标准的洗牌算法是 Jon-Skeet 在Is using Random and OrderBy 一个好的洗牌算法中描述的 Fisher-Yates 洗牌?.

于 2013-06-15T10:08:46.920 回答
1

首先,您可以使用这种方便的扩展方法,称为 Swap:

public static void Swap<T>(this IList<T> source, int one, int two)
{
    T temp = source[one];
    source[one] = source[two];
    source[two] = temp;
}

现在,您的代码应该很简单:

public void Shuffle()
{
    int count = kaarten.Count;
    Random rnd = new Random();
    for (int i = 0; i < 1000; i++)
    {
        kaarten.Swap(rnd.Next(0, count), rnd.Next(0, count));
    }
}
于 2013-06-15T10:08:19.640 回答
1

它打乱了arraylist元素

public void Shuffle(System.Collections.ArrayList elements)
    {
        int temp;
        Random randomNumber=new Random();
        for (int n = elements.Count; n > 1; )
        {
            int k = randomNumber.Next(n); //returning random number less than the value of 'n'
            --n; //decrease radom number generation area by 1 
            //swap the last and selected values
            temp = Convert.ToInt16(elements[n]);
            elements[n] = elements[k];
            elemetns[k] = temp;
        }
    }
于 2013-06-15T10:14:03.957 回答
1

费舍尔-耶茨洗牌

步骤 1. 从牌组中随机抽出一张牌

步骤 2. 把它放在新的甲板上

步骤 3. 如果甲板不为空,则重新迭代 1 和 2

将此添加到您的班级

public List<Card> Shuffle(List<Card> deck){
    List<Card> Shuffeled = new List<Card>();
    int deckSize = deck.Count;
    int selection = 0;
    Random rand = new Random();
    for(int i = 0; i < deckSize; i++){
        selection = rand.next(deck.Count-1);
        Shuffeled.Add(deck[selection]);
        deck.RemoveAt(selection);
    }
    return Shuffeled;
}

来自你的游戏通话kaarten = Shuffle(kaarten);

于 2013-06-15T10:36:57.220 回答