在c#中随机化泛型列表顺序的最佳方法是什么?我在一个列表中有一个有限的75个数字集,我想随机分配一个顺序,以便为彩票类型的应用程序绘制它们。


当前回答

 public Deck(IEnumerable<Card> initialCards) 
    {
    cards = new List<Card>(initialCards);
    public void Shuffle() 
     }
    {
        List<Card> NewCards = new List<Card>();
        while (cards.Count > 0) 
        {
            int CardToMove = random.Next(cards.Count);
            NewCards.Add(cards[CardToMove]);
            cards.RemoveAt(CardToMove);
        }
        cards = NewCards;
    }

public IEnumerable<string> GetCardNames() 

{
    string[] CardNames = new string[cards.Count];
    for (int i = 0; i < cards.Count; i++)
    CardNames[i] = cards[i].Name;
    return CardNames;
}

Deck deck1;
Deck deck2;
Random random = new Random();

public Form1() 
{

InitializeComponent();
ResetDeck(1);
ResetDeck(2);
RedrawDeck(1);
 RedrawDeck(2);

}



 private void ResetDeck(int deckNumber) 
    {
    if (deckNumber == 1) 
{
      int numberOfCards = random.Next(1, 11);
      deck1 = new Deck(new Card[] { });
      for (int i = 0; i < numberOfCards; i++)
           deck1.Add(new Card((Suits)random.Next(4),(Values)random.Next(1, 14)));
       deck1.Sort();
}


   else
    deck2 = new Deck();
 }

private void reset1_Click(object sender, EventArgs e) {
ResetDeck(1);
RedrawDeck(1);

}

private void shuffle1_Click(object sender, EventArgs e) 
{
    deck1.Shuffle();
    RedrawDeck(1);

}

private void moveToDeck1_Click(object sender, EventArgs e) 
{

    if (listBox2.SelectedIndex >= 0)
    if (deck2.Count > 0) {
    deck1.Add(deck2.Deal(listBox2.SelectedIndex));

}

    RedrawDeck(1);
    RedrawDeck(2);

}

其他回答

如果我们只需要以完全随机的顺序洗牌项目(只是在一个列表中混合项目),我更喜欢这个简单而有效的代码,按guid排序项目…

var shuffledcards = cards.OrderBy(a => Guid.NewGuid()).ToList();

正如人们在评论中指出的那样,guid不能保证是随机的,所以我们应该使用真正的随机数生成器:

private static Random rng = new Random();
...
var shuffledcards = cards.OrderBy(a => rng.Next()).ToList();

我们可以为List使用扩展方法,并使用线程安全的随机生成器组合。我在NuGet上打包了一个改进的版本,并在GitHub上提供源代码。NuGet版本包含可选的密码强随机。

Pre -。NET 6.0版本:

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Shuffle<T>(this IList<T> list)
{
    if (list == null) throw new ArgumentNullException(nameof(list));
    int n = list.Count;
    while (n > 1)
    {
        int k = ThreadSafeRandom.Instance.Next(n--);
        (list[n], list[k]) = (list[k], list[n]);
    }
}

internal class ThreadSafeRandom
{
    public static Random Instance => _local.Value;

    private static readonly Random _global = new Random();
    private static readonly ThreadLocal<Random> _local = new ThreadLocal<Random>(() =>
    {
        int seed;
        lock (_global)
        {
            seed = _global.Next();
        }
        return new Random(seed);
    });
}

在。net 6.0或更高版本上:

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Shuffle<T>(this IList<T> list)
{
    ArgumentNullException.ThrowIfNull(list);
    int n = list.Count;
    while (n > 1)
    {
        int k = Random.Shared.Next(n--);
        (list[n], list[k]) = (list[k], list[n]);
    }
}

通过NuGet安装库以获得更多功能。

如果您有一个固定的数字(75),您可以创建一个包含75个元素的数组,然后枚举您的列表,将元素移动到数组中的随机位置。您可以使用Fisher-Yates shuffle生成列表号到数组索引的映射。

你的问题是如何随机化一个列表。这意味着:

所有独特的组合都应该是可能发生的 所有唯一的组合应该出现在相同的分布(也就是无偏倚)。

由于“随机”,这个问题的大量答案不满足上述两个要求。

下面是一个紧凑的、无偏倚的伪随机函数,遵循Fisher-Yates shuffle方法。

public static void Shuffle<T>(this IList<T> list, Random rnd)
{
    for (var i = list.Count-1; i > 0; i--)
    {
        var randomIndex = rnd.Next(i + 1); //maxValue (i + 1) is EXCLUSIVE
        list.Swap(i, randomIndex); 
    }
}

public static void Swap<T>(this IList<T> list, int indexA, int indexB)
{
   var temp = list[indexA];
   list[indexA] = list[indexB];
   list[indexB] = temp;
}

这是我最喜欢的shuffle方法,当不需要修改原始的时候。它是Fisher-Yates“由内到外”算法的变体,适用于任何可枚举序列(源的长度不需要从一开始就知道)。

public static IList<T> NextList<T>(this Random r, IEnumerable<T> source)
{
  var list = new List<T>();
  foreach (var item in source)
  {
    var i = r.Next(list.Count + 1);
    if (i == list.Count)
    {
      list.Add(item);
    }
    else
    {
      var temp = list[i];
      list[i] = item;
      list.Add(temp);
    }
  }
  return list;
}

该算法还可以通过分配一个从0到length - 1的范围来实现,并通过将随机选择的索引与最后一个索引交换来随机耗尽索引,直到所有索引都被选中一次。上面的代码完成了完全相同的事情,但没有额外的分配。非常简洁。

With regards to the Random class it's a general purpose number generator (and If I was running a lottery I'd consider using something different). It also relies on a time based seed value by default. A small alleviation of the problem is to seed the Random class with the RNGCryptoServiceProvider or you could use the RNGCryptoServiceProvider in a method similar to this (see below) to generate uniformly chosen random double floating point values but running a lottery pretty much requires understanding randomness and the nature of the randomness source.

var bytes = new byte[8];
_secureRng.GetBytes(bytes);
var v = BitConverter.ToUInt64(bytes, 0);
return (double)v / ((double)ulong.MaxValue + 1);

生成随机双精度(仅在0和1之间)的目的是用于扩展到整数解。如果你需要从一个基于随机双x的列表中选择一个东西,它总是0 <= x && x < 1是很简单的。

return list[(int)(x * list.Count)];

享受吧!