【问题标题】:Making a shuffle method [duplicate]制作洗牌方法[重复]
【发布时间】:2013-06-15 10:04:18
【问题描述】:

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

    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)];
        }
    }

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

【问题讨论】:

标签: c# memory shuffle


【解决方案1】:

考虑到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 变量,就像在交换算法中几乎可以看到的任何地方一样。

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

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

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

【讨论】:

  • OrderBy(Random.Next) 不是一个好主意。
  • @newStackExchangeInstance 我同意,但它可能适合简单纸牌游戏的需求。
  • @newStackExchangeInstance 虽然在 LINQ 语句中产生副作用很难看,但它不会导致任何实际问题。
【解决方案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 次。标准的洗牌算法是一种 Fisher-Yates 洗牌,Jon-Skeet 在Is using Random and OrderBy a good shuffle algorithm? 上进行了描述。

【讨论】:

  • 看到并删除了评论和 -1。
【解决方案3】:

它洗牌数组列表元素

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

【讨论】:

  • 阵列列表?你在开玩笑吗?
  • @newStackExchangeInstance 你也复制粘贴了。他只是用一篇过时的文章做到了。
  • 但是逻辑是一样的,换个别的试试吧。
  • @harhar 你至少可以投资于改变你的答案以使其更合适(不要让 OP 为你做)。
  • @YoryeNathan 实际上,我只是复制了 swap 方法,然后由于比重新输入更麻烦,因此对其进行了完全编辑。 shuffle 方法是 OP 的一半 C&P(几乎只是定义),一半是固定的。冷静下来。
【解决方案4】:

首先,您可以使用这种方便的扩展方法,称为 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));
    }
}

【讨论】:

  • 如果需要,如果在实际交换过程中无论如何都会抛出异常,为什么要检查异常?
  • @YoryeNathan 没有理由,我只是复制+粘贴了这段代码。
  • 您可以使用Count-1 而不是Count,让最后一张卡片保持不变。 Random.Next 的上限是独占的。
  • @CodesInChaos 不知道,谢谢。
【解决方案5】:

Fisher-Yates 洗牌

步骤 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);

【讨论】:

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