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

时间:2013-06-06 13:53:08

标签: c# list random xna tile

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

我知道如何使用Random方法为单个图块添加一个数字来改变它的外观,但是我对如何使用Random方法无能为力列表存储多个图块的位置。如何为列表中的每个条目分配一个唯一的随机数?

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

3 个答案:

答案 0 :(得分:2)

看看Fisher-Yates shuffle。它非常容易使用,如果我正确地阅读你的问题,应该对你有用。

答案 1 :(得分:1)

如果您有这样的事情:

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

它做什么,它计算下一个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

所以在我看来,对于目前的使用情况(游戏中的随机播放室号码),guid是合适的解决方案。

答案 2 :(得分:1)

创建一个包含30个连续数字的数组,镜像您的数组。然后选择一个你喜欢的数组重组解决方案,例如:

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

然后tile [23]的编号为numberArray [23]。