【问题标题】:How to: Assign a unique number to every entry in a list?如何:为列表中的每个条目分配一个唯一编号?
【发布时间】:2013-06-06 13:53:08
【问题描述】:

我想要的是制作瓷砖。这些图块(大约 30 个)在游戏中应该有一个固定的位置,但是每次我加载游戏时,它们都应该有随机数,这会影响它们的图形外观。

我知道如何使用Random 方法为单个图块指定一个数字以更改其外观,但如果我要制作一个存储位置的列表,我将如何使用Random 方法一无所知的多个瓷砖。如何为列表中的每个条目分配一个唯一的随机数?

我的游戏需要这个,你在一个平面 2D 地图中,生成随机类型的房间(宝藏室、竞技场房间等),你要探索。

【问题讨论】:

  • 遍历您的列表并使用键值对分配随机值。
  • 随机数不必是唯一的
  • 在我看来,这里 90% 的关于随机数的问题都是以下三件事之一:(1)错误地播种 RNG(比如将 srand() 放入循环中)。 (2) 需要添加到恒定总和的随机数,以及 (3) 将随机数生成与混洗混淆。这是第 3 点——您不希望图块具有随机位置,而是希望它们具有随机顺序的所有标准位置。将这些坐标保存在一个数组中,将它们打乱,然后将其应用于图块。

标签: c# list random xna tile


【解决方案1】:

看看Fisher-Yates shuffle。如果我没看错你的问题,它非常易于使用,应该很适合你。

【讨论】:

    【解决方案2】:

    制作一个包含 30 个连续数字的数组,镜像您的瓷砖数组。然后选择一个你喜欢的数组改组解决方案,例如,这里:

    http://forums.asp.net/t/1778021.aspx/1

    那么 tile[23] 的编号将是 numberArray[23]。

    【讨论】:

    • 不仅仅是发布答案的链接,请在帖子本身中包含答案的详细信息(适当注明出处)。重要的是答案是独立的,虽然使用链接是可以的,但不应该要求读者点击链接来获得问题的答案。
    【解决方案3】:

    如果你有这样的事情:

    public class Tile 
    {
         public int Number {get;set;} 
         ...
    }
    

    你可以这样做:

    var numbers = Enumerable
      .Range(1, tilesList.Count)  // generates list of sequential numbers
      .OrderBy(x => Guid.NewGuid()) // shuffles the list
      .ToList();
    
    for (int i = 0; i < tiles.Count; i++) 
    {
        tile[i].Number = numbers[i];
    }
    

    我知道,Guid 不是 Random 的替代方案,但它应该适合这种情况。

    更新:只要答案被否决,我就编写了简单的测试,以检查 Guid 是否不可用于洗牌数组:

    var larger = 0;
    var smaller = 0;
    
    var start = DateTime.Now;
    
    var guid = Guid.NewGuid();
    
    for (int i = 0; i < 10000000; i++)
    {
        var nextGuid = Guid.NewGuid();
        if (nextGuid.CompareTo(guid) < 0)
        {
            larger++;
        }
        else
        {
            smaller++;
        }
    
        guid = nextGuid;
    }
    
    Console.WriteLine("larger: {0}", larger);
    Console.WriteLine("smaller: {0}", smaller);
    
    Console.WriteLine("took seconds: {0}", DateTime.Now - start);
    
    Console.ReadKey();
    

    它的作用是计算下一个 guid 比当前小多少倍,以及大多少倍。在完美的情况下,应该有相同数量的较大和较小的下一个 guid,这表明这两个事件(当前 guid 和下一个 guid)是独立的。还测量了时间,以确保它不会太慢。

    并得到以下结果(有 1000 万个 guid):

    更大:5000168
    较小:4999832
    花了几秒钟:00:00:01.1980686

    另一个测试是直接比较 Fisher-Yates 和 Guid 改组:

    static void Main(string[] args)
        {
            var numbers = Enumerable.Range(1, 7).ToArray();
            var originalNumbers = numbers.OrderBy(x => Guid.NewGuid()).ToList();
    
            var foundAfterListUsingGuid = new List<int>();
            var foundAfterListUsingShuffle = new List<int>();
    
            for (int i = 0; i < 100; i++)
            {
                var foundAfter = 0;
                while (!originalNumbers.SequenceEqual(numbers.OrderBy(x => Guid.NewGuid())))
                {
                    foundAfter++;                    
                }
    
                foundAfterListUsingGuid.Add(foundAfter);
    
                foundAfter = 0;
    
                var shuffledNumbers = Enumerable.Range(1, 7).ToArray();
                while (!originalNumbers.SequenceEqual(shuffledNumbers))
                {
                    foundAfter++;
                    Shuffle(shuffledNumbers);
                }
    
                foundAfterListUsingShuffle.Add(foundAfter);
            }
    
            Console.WriteLine("Average matching order (Guid): {0}", foundAfterListUsingGuid.Average());
            Console.WriteLine("Average matching order (Shuffle): {0}", foundAfterListUsingShuffle.Average());
            Console.ReadKey();
        }
    
        static Random _random = new Random();
    
        public static void Shuffle<T>(T[] array)
        {
            var random = _random;
            for (int i = array.Length; i > 1; i--)
            {
                // Pick random element to swap.
                int j = random.Next(i); // 0 <= j <= i-1
                // Swap.
                T tmp = array[j];
                array[j] = array[i - 1];
                array[i - 1] = tmp;
            }
        }
    

    “直接比较”的意思是,我正在生成打乱的序列并尝试再次打乱以获得相同的序列,并假设我需要生成相同序列的尝试越多,随机性就越好(这不是必要的数学正确假设,我认为这是过于简单化了)。

    因此,为减少错误而进行 1000 次迭代的小集合的结果是:

    平均匹配顺序(Guid):5015.097
    平均匹配顺序(Shuffle):4969.424

    所以,如果我的指标是正确的,Guid 会更好地执行事件:)

    经过 10000 次迭代,它们更接近了:

    平均匹配顺序(Guid):5079.9283
    平均匹配顺序(Shuffle):4940.749

    所以在我看来,对于当前的使用情况(游戏中的随机房间号),向导是合适的解决方案。

    【讨论】:

    • 但是说,如果我想使用Random,因为我可能想要多个宝藏室,我该怎么做?我知道这不是唯一,而是唯一,我的意思是列表的所有条目都应该有相同的数字。
    • @Neophyte - “唯一,我的意思是所有列表的条目都应该有相同的数字” - 我认为这是一个完全相反的唯一定义:)只要 Enumerable.Range 创建列表从 1 到titlesList.Length 的数字,该列表中的所有数字都是唯一的。然后我使用 Guid 对它们进行洗牌,它提供了足够不可预测的序列,仍然保持唯一性。
    • 我已经完成了很多工作,但我仍然不明白最后一段代码的作用(for-loop)。我有这样的变量:room_position(跟踪vector2s 的列表)和room_type(描述房间属性的int)。所以我想随机哪个数字得到哪个位置。 var 似乎占据了所有房间,并根据有多少房间给他们一个随机数字,对吧?
    • GUID 不是随机的,它们只是唯一的,不应用作随机数生成器。
    • @Servy> 我用一些非常简单的随机性测试更新了我的答案。
    猜你喜欢
    • 2017-07-10
    • 1970-01-01
    • 1970-01-01
    • 2021-06-12
    • 1970-01-01
    • 2016-10-19
    • 2021-11-06
    • 1970-01-01
    • 2012-09-04
    相关资源
    最近更新 更多