IEnumerable不会洗牌,除非我设置了断点

本文关键字:非我 设置 断点 IEnumerable | 更新日期: 2023-09-27 18:08:53

我正在尝试使用fisher-yates shuffle来打乱列表中的元素。然而,元素似乎不会洗牌,除非我在应该洗牌的点上放置一个断点。我尝试异步执行shuffle,但运气不好(也许我做错了(。

混洗算法如下:

// Uses Fisher-Yates shuffle to swap elements
    public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source)
    {
        Random rng = new Random();
        T[] elements = source.ToArray();
        for (int i = elements.Length - 1; i >= 0; i--)
        {
            int j = rng.Next(i + 1);
            yield return elements[j];
            elements[j] = elements[i];
        }
    }

我这样洗牌:

imageDataList= imageDataList.Shuffle().ToList();

我像这样异步地洗牌:

imageDataList = await Task.Run(() => imageDataList.Shuffle().ToList());

以下是我的代码片段:

GameData gameData = null;
ImageData imageData = null;
ToneData toneData = null;
ImageToneData imageToneData = null;
LevelData levelData = null;
List<ImageData> imageDataList = new List<ImageData>();
List<ToneData> toneDataList = new List<ToneData>();
List<ImageToneData> imageToneDataList = new List<ImageToneData>();
List<LevelData> levelDataList = new List<LevelData>();
 // Add images to a list of ImageData contracts
    foreach (GameImage gameImage in game.GameImages)
    {
        imageData = new ImageData()
        {
            ImageId = gameImage.Image.ImageId,
            ImageFileName = gameImage.Image.ImageFileName
        };
        imageDataList.Add(imageData);
    }
    // Add tones to a list of ToneData contracts
    foreach (GameTone gameTone in game.GameTones)
    {
        toneData = new ToneData()
        {
            ToneId = gameTone.Tone.ToneId,
            ToneFileName = gameTone.Tone.ToneFileName
        };
        toneDataList.Add(toneData);
    }

    //Randomize image and tone association
    imageDataList = imageDataList.Shuffle().ToList();
    toneDataList = toneDataList.Shuffle().ToList();
    // Combine imageData and toneData and assign a position
    for (int i = 0; i < game.NumLevels; i++)
    {
        imageToneData = new ImageToneData()
        {
            Image = imageDataList.ElementAt(i),
            Tone = toneDataList.ElementAt(i),
            Position = (i + 1)
        };
        imageToneDataList.Add(imageToneData);
    }
    foreach (GameLevel level in game.GameLevels)
    {
        //Randomize image/tone (already fixed association)
        imageToneDataList = imageToneDataList.Shuffle().ToList();
        levelData = new LevelData()
        {
            GameLevelId = level.GameLevelId,
            Level = level.Level,
            UniqueRounds = level.UniqueRounds,
            Rounds = level.Rounds,
            NumImages = level.NumImages,
            ImageTones = imageToneDataList.Take(level.NumImages)
        };
        levelDataList.Add(levelData);
    }
gameData = new GameData()
{
    NumLevels = game.NumLevels,
    SelectionTime = game.SelectionTime,
    Levels = levelDataList
};

我的数据合约如下:

  [DataContract]
    public class GameData
    {
        [DataMember]
        public int NumLevels { get; set; }
        [DataMember]
        public int? SelectionTime { get; set; }
        [DataMember]
        public IEnumerable<LevelData> Levels { get; set; }
    }
[DataContract]
public class LevelData
{
    [DataMember]
    public int GameLevelId { get; set; }
    [DataMember]
    public int Level { get; set; }
    [DataMember]
    public bool UniqueRounds { get; set; }
    [DataMember]
    public int Rounds { get; set; }
    [DataMember]
    public int NumImages { get; set; }
    [DataMember]
    public IEnumerable<ImageToneData> ImageTones { get; set; }
}

[DataContract]
public class ImageToneData
{
    [DataMember]
    public ImageData Image { get; set; }
    [DataMember]
    public ToneData Tone { get; set; }
    [DataMember]
    public int? Position { get; set; }
}

[DataContract]
public class ImageData
{
    [DataMember]
    public int ImageId { get; set; }
    [DataMember]
    public string ImageFileName { get; set; }
}

[DataContract]
public class ToneData
{
    [DataMember]
    public int ToneId { get; set; }
    [DataMember]
    public string ToneFileName { get; set; }
}

非常感谢您的帮助!

IEnumerable不会洗牌,除非我设置了断点

我的猜测是imageDataListtoneDataList这两个列表实际上正在被打乱。我怀疑它们看起来没有被打乱,因为每次运行该代码时,当您将两个列表组合到imageToneDataList中时,相同的音调总是与上次运行代码时的相同图像配对。这是因为每次调用Shuffle方法时都会实例化一个新的RandomRandom使用系统时钟来生成其种子。因此,如果您背靠背实例化两个Random对象(就像您通过连续调用Shuffle两次所做的那样(,它们很有可能具有相同的种子。这意味着他们将产生相同的随机数。这意味着你的两个列表正以完全相同的顺序被打乱。这就解释了为什么当你只洗牌一个列表时,它似乎能正常工作。当你把混洗过的列表和没有混洗的列表合并到imageToneDataList中时,你会得到你期望的随机结果。这也解释了为什么在使用断点时它有效。它们在第一次调用Shuffle和第二次调用之间的延迟足够长,可以创建一个带有新种子的Random对象。

为了获得您期望的行为,您需要有一个Random的单个实例——可能是包含扩展方法的类中的一个静态字段。

我会把方法改成这样:

public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source)
{
    Random rng = new Random();
    List<T> elements = source.ToList();
    while (elements.Count > 0)
    {
        int i = rng.Next(elements.Count);
        T item = elements[i];
        elements.RemoveAt(i);
        yield return item;
    }
}