如何:为列表中的每个条目分配唯一的编号

本文关键字:分配 编号 唯一 列表 如何 | 更新日期: 2023-09-27 18:17:18

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

我知道如何使用Random方法给单个瓷砖一个数字来改变它的外观,但是如果我要制作一个存储多个瓷砖位置的列表,我就不知道如何使用Random方法。如何为列表中的每个条目分配唯一的随机数?

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

如何:为列表中的每个条目分配唯一的编号

看一下Fisher-Yates洗牌。它非常容易使用,如果我没看错你的问题的话,它应该很适合你。

创建一个包含30个连续数字的数组,与您的tile数组相对应。然后从下面选择一个您喜欢的数组洗牌解决方案,例如:

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

则tile[23]的编号为numberArray[23].

如果你有这样的东西:

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的替代品,但它应该适合这个场景。

更新:只要答案被否决,我写了一个简单的测试,检查是否Guids不能用于洗牌数组:

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();

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

得到了以下结果(有1000万个指南):


大: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

所以在我看来,对于目前的使用(在游戏中洗牌房间号码),指南是合适的解决方案。