随机化列表

发布于 2024-07-08 17:54:30 字数 81 浏览 8 评论 0原文

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

What is the best way to randomize the order of a generic list in C#? I've got a finite set of 75 numbers in a list I would like to assign a random order to, in order to draw them for a lottery type application.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(30

祁梦 2024-07-15 17:54:31

我对这个简单算法的所有笨拙版本感到有点惊讶。 Fisher-Yates(或 Knuth shuffle)有点棘手,但非常紧凑。 为什么它很棘手? 因为您需要注意随机数生成器 r(a,b) 返回的值是包含还是不包含 b。 我还编辑了维基百科描述,这样人们就不会盲目地遵循伪代码并产生难以检测的错误。 对于 .Net,Random.Next(a,b) 返回不包括 b 的数字,所以言归正传,以下是它在 C#/.Net 中的实现方式:

public static void Shuffle<T>(this IList<T> list, Random? rnd = null)
{
    rnd ??= Random.Shared;
    // [edited]
    for (int i = list.Count - 1; i > 0; i--)
        list.Swap(i, rnd.Next(i + 1));
}

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

尝试此代码

I'm bit surprised by all the clunky versions of this simple algorithm here. Fisher-Yates (or Knuth shuffle) is bit tricky but very compact. Why is it tricky? Because your need to pay attention to whether your random number generator r(a,b) returns value where b is inclusive or exclusive. I've also edited Wikipedia description so people don't blindly follow pseudocode there and create hard to detect bugs. For .Net, Random.Next(a,b) returns number exclusive of b so without further ado, here's how it can be implemented in C#/.Net:

public static void Shuffle<T>(this IList<T> list, Random? rnd = null)
{
    rnd ??= Random.Shared;
    // [edited]
    for (int i = list.Count - 1; i > 0; i--)
        list.Swap(i, rnd.Next(i + 1));
}

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

Try this code.

浮生面具三千个 2024-07-15 17:54:31

IEnumerable的扩展方法:

public static IEnumerable<T> Randomize<T>(this IEnumerable<T> source)
{
    Random rnd = new Random();
    return source.OrderBy<T, int>((item) => rnd.Next());
}

Extension method for IEnumerable:

public static IEnumerable<T> Randomize<T>(this IEnumerable<T> source)
{
    Random rnd = new Random();
    return source.OrderBy<T, int>((item) => rnd.Next());
}
野侃 2024-07-15 17:54:31

想法是获取具有项目和随机顺序的匿名对象,然后按此顺序重新排序项目并返回值:

var result = items.Select(x => new { value = x, order = rnd.Next() })
            .OrderBy(x => x.order).Select(x => x.value).ToList()

Idea is get anonymous object with item and random order and then reorder items by this order and return value:

var result = items.Select(x => new { value = x, order = rnd.Next() })
            .OrderBy(x => x.order).Select(x => x.value).ToList()
妄想挽回 2024-07-15 17:54:31
    public static List<T> Randomize<T>(List<T> list)
    {
        List<T> randomizedList = new List<T>();
        Random rnd = new Random();
        while (list.Count > 0)
        {
            int index = rnd.Next(0, list.Count); //pick a random item from the master list
            randomizedList.Add(list[index]); //place it at the end of the randomized list
            list.RemoveAt(index);
        }
        return randomizedList;
    }
    public static List<T> Randomize<T>(List<T> list)
    {
        List<T> randomizedList = new List<T>();
        Random rnd = new Random();
        while (list.Count > 0)
        {
            int index = rnd.Next(0, list.Count); //pick a random item from the master list
            randomizedList.Add(list[index]); //place it at the end of the randomized list
            list.RemoveAt(index);
        }
        return randomizedList;
    }
暮色兮凉城 2024-07-15 17:54:31

从 .NET 8 开始,您可以使用 Shuffle()

//Argument is Span<T> or T[]
Random.Shared.Shuffle(mySpan);

或者,对于加密的强随机性:

//Argument is Span<T>
RandomNumberGenerator.Shuffle(mySpan);

对于列表,您必须首先创建一个数组 (myList.ToArray()),如上所示进行随机播放然后从打乱后的数组中创建一个新列表

Starting in .NET 8, you can use Shuffle():

//Argument is Span<T> or T[]
Random.Shared.Shuffle(mySpan);

Or, for a cryptographically strong randomness:

//Argument is Span<T>
RandomNumberGenerator.Shuffle(mySpan);

For a list, you would have to create an array first (myList.ToArray()) shuffle as above and then create a new list from the shuffled array

春庭雪 2024-07-15 17:54:31

编辑
RemoveAt 是我之前版本中的一个弱点。 这个解决方案克服了这个问题。

public static IEnumerable<T> Shuffle<T>(
        this IEnumerable<T> source,
        Random generator = null)
{
    if (generator == null)
    {
        generator = new Random();
    }

    var elements = source.ToArray();
    for (var i = elements.Length - 1; i >= 0; i--)
    {
        var swapIndex = generator.Next(i + 1);
        yield return elements[swapIndex];
        elements[swapIndex] = elements[i];
    }
}

请注意可选的随机生成器,如果Random的基本框架实现不是线程安全的或加密强度不足以满足您的需求,您可以将您的实现注入到操作中。

可以在此答案中找到线程安全加密强Random实现的合适实现。


这是一个想法,以(希望)有效的方式扩展 IList。

public static IEnumerable<T> Shuffle<T>(this IList<T> list)
{
    var choices = Enumerable.Range(0, list.Count).ToList();
    var rng = new Random();
    for(int n = choices.Count; n > 1; n--)
    {
        int k = rng.Next(n);
        yield return list[choices[k]];
        choices.RemoveAt(k);
    }

    yield return list[choices[0]];
}

EDIT
The RemoveAt is a weakness in my previous version. This solution overcomes that.

public static IEnumerable<T> Shuffle<T>(
        this IEnumerable<T> source,
        Random generator = null)
{
    if (generator == null)
    {
        generator = new Random();
    }

    var elements = source.ToArray();
    for (var i = elements.Length - 1; i >= 0; i--)
    {
        var swapIndex = generator.Next(i + 1);
        yield return elements[swapIndex];
        elements[swapIndex] = elements[i];
    }
}

Note the optional Random generator, if the base framework implementation of Random is not thread-safe or cryptographically strong enough for your needs, you can inject your implementation into the operation.

A suitable implementation for a thread-safe cryptographically strong Random implementation can be found in this answer.


Here's an idea, extend IList in a (hopefully) efficient way.

public static IEnumerable<T> Shuffle<T>(this IList<T> list)
{
    var choices = Enumerable.Range(0, list.Count).ToList();
    var rng = new Random();
    for(int n = choices.Count; n > 1; n--)
    {
        int k = rng.Next(n);
        yield return list[choices[k]];
        choices.RemoveAt(k);
    }

    yield return list[choices[0]];
}

‖放下 2024-07-15 17:54:31

可以使用 morelinq 包中的 Shuffle 扩展方法,它适用于 IEnumerables

安装包morelinq

using MoreLinq;
...    
var randomized = list.Shuffle();

One can use the Shuffle extension methond from morelinq package, it works on IEnumerables

install-package morelinq

using MoreLinq;
...    
var randomized = list.Shuffle();
多孤肩上扛 2024-07-15 17:54:31

当希望不修改原始内容时,这是我首选的随机播放方法。 它是 Fisher–Yates "inside- 的变体- out”算法适用于任何可枚举序列(source的长度不需要从一开始就知道)。

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;
}

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

对于 Random 类,它是一个通用数字生成器(如果我正在运行彩票,我会考虑使用不同的东西)。 默认情况下,它还依赖于基于时间的种子值。 问题的一个小缓解是使用 RNGCryptoServiceProviderRandom 类提供种子,或者您可以在与此类似的方法中使用 RNGCryptoServiceProvider(请参阅生成统一选择的随机双浮点值,但运行彩票几乎需要了解随机性和随机源的性质。

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

享受!

This is my preferred method of a shuffle when it's desirable to not modify the original. It's a variant of the Fisher–Yates "inside-out" algorithm that works on any enumerable sequence (the length of source does not need to be known from start).

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;
}

This algorithm can also be implemented by allocating a range from 0 to length - 1 and randomly exhausting the indices by swapping the randomly chosen index with the last index until all indices have been chosen exactly once. This above code accomplishes the exact same thing but without the additional allocation. Which is pretty neat.

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

The point of generating a random double (between 0 and 1 exclusively) is to use to scale to an integer solution. If you need to pick something from a list based on a random double x that's always going to be 0 <= x && x < 1 is straight forward.

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

Enjoy!

晨曦÷微暖 2024-07-15 17:54:31

如果您不介意使用两个列表,那么这可能是最简单的方法,但可能不是最有效或不可预测的方法:

List<int> xList = new List<int>() { 1, 2, 3, 4, 5 };
List<int> deck = new List<int>();

foreach (int xInt in xList)
    deck.Insert(random.Next(0, deck.Count + 1), xInt);

If you don't mind using two Lists, then this is probably the easiest way to do it, but probably not the most efficient or unpredictable one:

List<int> xList = new List<int>() { 1, 2, 3, 4, 5 };
List<int> deck = new List<int>();

foreach (int xInt in xList)
    deck.Insert(random.Next(0, deck.Count + 1), xInt);
耳钉梦 2024-07-15 17:54:31

只是想建议使用 IComparerList.Sort() 的变体:

public class RandomIntComparer : IComparer<int>
{
    private readonly Random _random = new Random();
    
    public int Compare(int x, int y)
    {
        return _random.Next(-1, 2);
    }
}

用法:

list.Sort(new RandomIntComparer());

Just wanted to suggest a variant using an IComparer<T> and List.Sort():

public class RandomIntComparer : IComparer<int>
{
    private readonly Random _random = new Random();
    
    public int Compare(int x, int y)
    {
        return _random.Next(-1, 2);
    }
}

Usage:

list.Sort(new RandomIntComparer());
与酒说心事 2024-07-15 17:54:31

您可以使用这个简单的扩展方法来实现这一点

public static class IEnumerableExtensions
{

    public static IEnumerable<t> Randomize<t>(this IEnumerable<t> target)
    {
        Random r = new Random();

        return target.OrderBy(x=>(r.Next()));
    }        
}

,并且可以通过执行以下操作来使用它

// use this on any collection that implements IEnumerable!
// List, Array, HashSet, Collection, etc

List<string> myList = new List<string> { "hello", "random", "world", "foo", "bar", "bat", "baz" };

foreach (string s in myList.Randomize())
{
    Console.WriteLine(s);
}

You can achieve that be using this simple extension method

public static class IEnumerableExtensions
{

    public static IEnumerable<t> Randomize<t>(this IEnumerable<t> target)
    {
        Random r = new Random();

        return target.OrderBy(x=>(r.Next()));
    }        
}

and you can use it by doing the following

// use this on any collection that implements IEnumerable!
// List, Array, HashSet, Collection, etc

List<string> myList = new List<string> { "hello", "random", "world", "foo", "bar", "bat", "baz" };

foreach (string s in myList.Randomize())
{
    Console.WriteLine(s);
}
焚却相思 2024-07-15 17:54:31

我们可以使用 List 的扩展方法并使用线程安全的随机生成器组合。 我在 NuGet 上打包了此版本的改进版本,源代码可在 NuGet ="https://github.com/MarkCiliaVincenti/ListShuffle" rel="nofollow noreferrer">GitHub。 NuGet 版本包含可选的加密强随机数。

.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 安装库更多功能。

We can use an extension method for List and use a thread-safe random generator combination. I've packaged an improved version of this on NuGet with the source code available on GitHub. The NuGet version contains optional cryptographically-strong random.

Pre-.NET 6.0 version:

[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);
    });
}

On .NET 6.0 or later:

[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]);
    }
}

Install the library via NuGet for more features.

避讳 2024-07-15 17:54:31

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

If you have a fixed number (75), you could create an array with 75 elements, then enumerate your list, moving the elements to randomized positions in the array. You can generate the mapping of list number to array index using the Fisher-Yates shuffle.

似梦非梦 2024-07-15 17:54:31

我通常使用:

var list = new List<T> ();
fillList (list);
var randomizedList = new List<T> ();
var rnd = new Random ();
while (list.Count != 0)
{
    var index = rnd.Next (0, list.Count);
    randomizedList.Add (list [index]);
    list.RemoveAt (index);
}

I usually use:

var list = new List<T> ();
fillList (list);
var randomizedList = new List<T> ();
var rnd = new Random ();
while (list.Count != 0)
{
    var index = rnd.Next (0, list.Count);
    randomizedList.Add (list [index]);
    list.RemoveAt (index);
}
妳是的陽光 2024-07-15 17:54:31

通过使用元组进行交换,您可以使 Fisher-Yates 洗牌变得更加简洁和富有表现力。

private static readonly Random random = new Random();

public static void Shuffle<T>(this IList<T> list)
{
    int n = list.Count;
    while (n > 1)
    {
        n--;
        int k = random.Next(n + 1);
        (list[k], list[n]) = (list[n], list[k]);
    }
}

You can make the Fisher-Yates shuffle more terse and expressive by using tuples for the swap.

private static readonly Random random = new Random();

public static void Shuffle<T>(this IList<T> list)
{
    int n = list.Count;
    while (n > 1)
    {
        n--;
        int k = random.Next(n + 1);
        (list[k], list[n]) = (list[n], list[k]);
    }
}
感悟人生的甜 2024-07-15 17:54:31

实现:

public static class ListExtensions
{
    public static void Shuffle<T>(this IList<T> list, Random random)
    {
        for (var i = list.Count - 1; i > 0; i--)
        {
            int indexToSwap = random.Next(i + 1);
            (list[indexToSwap], list[i]) = (list[i], list[indexToSwap]);
        }
    }
}

示例:

var random = new Random();
var array = new [] { 1, 2, 3 };
array.Shuffle(random);
foreach (var item in array) {
    Console.WriteLine(item);
}

.NET Fiddle 中的演示

Implementation:

public static class ListExtensions
{
    public static void Shuffle<T>(this IList<T> list, Random random)
    {
        for (var i = list.Count - 1; i > 0; i--)
        {
            int indexToSwap = random.Next(i + 1);
            (list[indexToSwap], list[i]) = (list[i], list[indexToSwap]);
        }
    }
}

Example:

var random = new Random();
var array = new [] { 1, 2, 3 };
array.Shuffle(random);
foreach (var item in array) {
    Console.WriteLine(item);
}

Demonstration in .NET Fiddle

久夏青 2024-07-15 17:54:31

接受的答案进行简单修改,返回一个新列表而不是就地工作,并接受更通用的IEnumerable 与许多其他 Linq 方法一样。

private static Random rng = new Random();

/// <summary>
/// Returns a new list where the elements are randomly shuffled.
/// Based on the Fisher-Yates shuffle, which has O(n) complexity.
/// </summary>
public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> list) {
    var source = list.ToList();
    int n = source.Count;
    var shuffled = new List<T>(n);
    shuffled.AddRange(source);
    while (n > 1) {
        n--;
        int k = rng.Next(n + 1);
        T value = shuffled[k];
        shuffled[k] = shuffled[n];
        shuffled[n] = value;
    }
    return shuffled;
}

A simple modification of the accepted answer that returns a new list instead of working in-place, and accepts the more general IEnumerable<T> as many other Linq methods do.

private static Random rng = new Random();

/// <summary>
/// Returns a new list where the elements are randomly shuffled.
/// Based on the Fisher-Yates shuffle, which has O(n) complexity.
/// </summary>
public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> list) {
    var source = list.ToList();
    int n = source.Count;
    var shuffled = new List<T>(n);
    shuffled.AddRange(source);
    while (n > 1) {
        n--;
        int k = rng.Next(n + 1);
        T value = shuffled[k];
        shuffled[k] = shuffled[n];
        shuffled[n] = value;
    }
    return shuffled;
}
陌生 2024-07-15 17:54:31
    List<T> OriginalList = new List<T>();
    List<T> TempList = new List<T>();
    Random random = new Random();
    int length = OriginalList.Count;
    int TempIndex = 0;

    while (length > 0) {
        TempIndex = random.Next(0, length);  // get random value between 0 and original length
        TempList.Add(OriginalList[TempIndex]); // add to temp list
        OriginalList.RemoveAt(TempIndex); // remove from original list
        length = OriginalList.Count;  // get new list <T> length.
    }

    OriginalList = new List<T>();
    OriginalList = TempList; // copy all items from temp list to original list.
    List<T> OriginalList = new List<T>();
    List<T> TempList = new List<T>();
    Random random = new Random();
    int length = OriginalList.Count;
    int TempIndex = 0;

    while (length > 0) {
        TempIndex = random.Next(0, length);  // get random value between 0 and original length
        TempList.Add(OriginalList[TempIndex]); // add to temp list
        OriginalList.RemoveAt(TempIndex); // remove from original list
        length = OriginalList.Count;  // get new list <T> length.
    }

    OriginalList = new List<T>();
    OriginalList = TempList; // copy all items from temp list to original list.
暗藏城府 2024-07-15 17:54:31

这是 Fisher-Yates shuffle 的一个实现,它允许指定要返回的元素数量; 因此,在获取所需数量的元素之前,无需先对整个集合进行排序。

交换元素的顺序与默认顺序相反; 并从第一个元素继续到最后一个元素,以便检索集合的子集会产生与打乱整个集合相同的(部分)序列:

collection.TakeRandom(5).SequenceEqual(collection.Shuffle().Take(5)); // true

该算法基于 Durstenfeld 的(现代)版本的 Fisher-Yates shuffle

public static IList<T> TakeRandom<T>(this IEnumerable<T> collection, int count, Random random) => shuffle(collection, count, random);
public static IList<T> Shuffle<T>(this IEnumerable<T> collection, Random random) => shuffle(collection, null, random);
private static IList<T> shuffle<T>(IEnumerable<T> collection, int? take, Random random)
{
    var a = collection.ToArray();
    var n = a.Length;
    if (take <= 0 || take > n) throw new ArgumentException("Invalid number of elements to return.");
    var end = take ?? n;
    for (int i = 0; i < end; i++)
    {
        var j = random.Next(i, n);
        (a[i], a[j]) = (a[j], a[i]);
    }

    if (take.HasValue) return new ArraySegment<T>(a, 0, take.Value);
    return a;
}

Here is an implementation of the Fisher-Yates shuffle that allows specification of the number of elements to return; hence, it is not necessary to first sort the whole collection before taking your desired number of elements.

The sequence of swapping elements is reversed from default; and proceeds from the first element to the last element, so that retrieving a subset of the collection yields the same (partial) sequence as shuffling the whole collection:

collection.TakeRandom(5).SequenceEqual(collection.Shuffle().Take(5)); // true

This algorithm is based on Durstenfeld's (modern) version of the Fisher-Yates shuffle on Wikipedia.

public static IList<T> TakeRandom<T>(this IEnumerable<T> collection, int count, Random random) => shuffle(collection, count, random);
public static IList<T> Shuffle<T>(this IEnumerable<T> collection, Random random) => shuffle(collection, null, random);
private static IList<T> shuffle<T>(IEnumerable<T> collection, int? take, Random random)
{
    var a = collection.ToArray();
    var n = a.Length;
    if (take <= 0 || take > n) throw new ArgumentException("Invalid number of elements to return.");
    var end = take ?? n;
    for (int i = 0; i < end; i++)
    {
        var j = random.Next(i, n);
        (a[i], a[j]) = (a[j], a[i]);
    }

    if (take.HasValue) return new ArraySegment<T>(a, 0, take.Value);
    return a;
}
如梦亦如幻 2024-07-15 17:54:31

这是一个高效的 Shuffler,它返回打乱值的字节数组。 它不会洗牌超过需要的数量。 它可以从之前停止的地方重新启动。 我的实际实现(未显示)是一个 MEF 组件,允许用户指定替换洗牌器。

    public byte[] Shuffle(byte[] array, int start, int count)
    {
        int n = array.Length - start;
        byte[] shuffled = new byte[count];
        for(int i = 0; i < count; i++, start++)
        {
            int k = UniformRandomGenerator.Next(n--) + start;
            shuffled[i] = array[k];
            array[k] = array[start];
            array[start] = shuffled[i];
        }
        return shuffled;
    }

`

Here's an efficient Shuffler that returns a byte array of shuffled values. It never shuffles more than is needed. It can be restarted from where it previously left off. My actual implementation (not shown) is a MEF component that allows a user specified replacement shuffler.

    public byte[] Shuffle(byte[] array, int start, int count)
    {
        int n = array.Length - start;
        byte[] shuffled = new byte[count];
        for(int i = 0; i < count; i++, start++)
        {
            int k = UniformRandomGenerator.Next(n--) + start;
            shuffled[i] = array[k];
            array[k] = array[start];
            array[start] = shuffled[i];
        }
        return shuffled;
    }

`

梦途 2024-07-15 17:54:31

我在网上找到了一个有趣的解决方案。

礼貌:https://improveandrepeat .com/2018/08/a-simple-way-to-shuffle-your-lists-in-c/

var shuffled = myList.OrderBy(x => Guid.NewGuid()).ToList() ;

I have found an interesting solution online.

Courtesy: https://improveandrepeat.com/2018/08/a-simple-way-to-shuffle-your-lists-in-c/

var shuffled = myList.OrderBy(x => Guid.NewGuid()).ToList();

腹黑女流氓 2024-07-15 17:54:31

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

  1. 所有独特的组合都应该有可能发生
  2. 所有独特的组合都应该以相同的分布出现(又称无偏)。

针对这个问题发布的大量答案不满足上述“随机”的两个要求。

这是遵循 Fisher-Yates 洗牌方法的紧凑、无偏伪随机函数。

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;
}

Your question is how to randomize a list. This means:

  1. All unique combinations should be possible of happening
  2. All unique combinations should occur with the same distribution (AKA being non-biased).

A large number of the answers posted for this question do NOT satisfy the two requirements above for being "random".

Here's a compact, non-biased pseudo-random function following the Fisher-Yates shuffle method.

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;
}
浅唱ヾ落雨殇 2024-07-15 17:54:31

.net 8.0(2023 年 11 月)开始,新的 Random.ShuffleRandomNumberGenerator.Shuffle(Span) 方法让您随机化一系列项目的顺序。

int[] myNumbers = LoadNumbers();
Random.Shared.Shuffle(myNumbers);
// myNumbers are now shuffled.

Beginning with .net 8.0 (nov. 2023) The new Random.Shuffle and RandomNumberGenerator.Shuffle<T>(Span<T>) methods let you randomize the order of a span of items.

int[] myNumbers = LoadNumbers();
Random.Shared.Shuffle(myNumbers);
// myNumbers are now shuffled.
著墨染雨君画夕 2024-07-15 17:54:31

使用 C#12,建议的 Fisher-Yates shuffle 可以变得更加简洁,无需临时变量:

public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source, Random random)
{
    var r = new List<T>(source);
    var a = source.Count();

    while (a > 1)
    {
        var b = random.Next(a--);
        (r[b], r[a]) = (r[a], r[b]);
    }

    return r;
}

另请注意,根据约定,Enumerable 扩展应返回一个新集合,而不是就地修改源集合。

With C#12, the proposed Fisher-Yates shuffle can be made a bit more concise, removing the need for a temporary variable:

public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source, Random random)
{
    var r = new List<T>(source);
    var a = source.Count();

    while (a > 1)
    {
        var b = random.Next(a--);
        (r[b], r[a]) = (r[a], r[b]);
    }

    return r;
}

Also note that per convention, Enumerable extensions should return a new collection instead of modifying the source collection in-place.

若言繁花未落 2024-07-15 17:54:31

这是执行此操作的线程安全方法:

public static class EnumerableExtension
{
    private static Random globalRng = new Random();

    [ThreadStatic]
    private static Random _rng;

    private static Random rng 
    {
        get
        {
            if (_rng == null)
            {
                int seed;
                lock (globalRng)
                {
                    seed = globalRng.Next();
                }
                _rng = new Random(seed);
             }
             return _rng;
         }
    }

    public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> items)
    {
        return items.OrderBy (i => rng.Next());
    }
}

Here's a thread-safe way to do this:

public static class EnumerableExtension
{
    private static Random globalRng = new Random();

    [ThreadStatic]
    private static Random _rng;

    private static Random rng 
    {
        get
        {
            if (_rng == null)
            {
                int seed;
                lock (globalRng)
                {
                    seed = globalRng.Next();
                }
                _rng = new Random(seed);
             }
             return _rng;
         }
    }

    public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> items)
    {
        return items.OrderBy (i => rng.Next());
    }
}
不羁少年 2024-07-15 17:54:31
 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);

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

}
原来是傀儡 2024-07-15 17:54:31

public List shufflelist(列表列表)
{
LetterClass 临时元素;
列表临时列表 = new List();
列表listcopy = new List();
国际兰特;

 foreach(列表中的 LetterClass 项) 
      { 
          添加(项目); 
      } 

      while (listcopy.Count != 0) 
      { 
          rand = Random.Range(0, listcopy.Count); 
          tempelement = listcopy[rand]; 
          templist.Add(listcopy[rand]); 
          listcopy.Remove(tempelement); 

      } 

      返回临时列表; 

  } 
  

public List shufflelist(List list)
{
LetterClass tempelement;
List templist = new List();
List listcopy = new List();
int rand;

    foreach (LetterClass item in list)
    {
        listcopy.Add(item);
    }

    while (listcopy.Count != 0)
    {
        rand = Random.Range(0, listcopy.Count);
        tempelement = listcopy[rand];
        templist.Add(listcopy[rand]);
        listcopy.Remove(tempelement);

    }

    return templist;

}
爱*していゐ 2024-07-15 17:54:31
private List<GameObject> ShuffleList(List<GameObject> ActualList) {


    List<GameObject> newList = ActualList;
    List<GameObject> outList = new List<GameObject>();

    int count = newList.Count;

    while (newList.Count > 0) {

        int rando = Random.Range(0, newList.Count);

        outList.Add(newList[rando]);

        newList.RemoveAt(rando);

     

    }

    return (outList);

}

用法 :

List<GameObject> GetShuffle = ShuffleList(ActualList);
private List<GameObject> ShuffleList(List<GameObject> ActualList) {


    List<GameObject> newList = ActualList;
    List<GameObject> outList = new List<GameObject>();

    int count = newList.Count;

    while (newList.Count > 0) {

        int rando = Random.Range(0, newList.Count);

        outList.Add(newList[rando]);

        newList.RemoveAt(rando);

     

    }

    return (outList);

}

usage :

List<GameObject> GetShuffle = ShuffleList(ActualList);
许仙没带伞 2024-07-15 17:54:30

使用基于 Fisher-Yates 的扩展方法随机播放任何 (I)List shuffle

private static Random rng = new Random();  

public static void Shuffle<T>(this IList<T> list)  
{  
    int n = list.Count;  
    while (n > 1) {  
        n--;  
        int k = rng.Next(n + 1);  
        T value = list[k];  
        list[k] = list[n];  
        list[n] = value;  
    }  
}

用法:

List<Product> products = GetProducts();
products.Shuffle();

上面的代码使用备受批评的System.Random方法来选择交换候选者。 它很快,但没有应有的随机性。 如果您需要在随机播放中获得更好的随机性,请使用 System.Security.Cryptography 中的随机数生成器,如下所示:

using System.Security.Cryptography;
...
public static void Shuffle<T>(this IList<T> list)
{
    RNGCryptoServiceProvider provider = new RNGCryptoServiceProvider();
    int n = list.Count;
    while (n > 1)
    {
        byte[] box = new byte[1];
        do provider.GetBytes(box);
        while (!(box[0] < n * (Byte.MaxValue / n)));
        int k = (box[0] % n);
        n--;
        T value = list[k];
        list[k] = list[n];
        list[n] = value;
    }
}

可以进行简单的比较 在此博客(WayBack Machine)。

编辑:自从几年前写下这个答案以来,很多人都评论或写信给我,指出我的比较中存在的巨大愚蠢缺陷。 他们当然是对的。 如果按预期方式使用 System.Random,则没有任何问题。 在上面的第一个示例中,我在 Shuffle 方法内部实例化了 rng 变量,如果要重复调用该方法,这会带来麻烦。 下面是一个固定的完整示例,基于今天从 @weston 收到的非常有用的评论。

程序.cs:

using System;
using System.Collections.Generic;
using System.Threading;

namespace SimpleLottery
{
  class Program
  {
    private static void Main(string[] args)
    {
      var numbers = new List<int>(Enumerable.Range(1, 75));
      numbers.Shuffle();
      Console.WriteLine("The winning numbers are: {0}", string.Join(",  ", numbers.GetRange(0, 5)));
    }
  }

  public static class ThreadSafeRandom
  {
      [ThreadStatic] private static Random Local;

      public static Random ThisThreadsRandom
      {
          get { return Local ?? (Local = new Random(unchecked(Environment.TickCount * 31 + Thread.CurrentThread.ManagedThreadId))); }
      }
  }

  static class MyExtensions
  {
    public static void Shuffle<T>(this IList<T> list)
    {
      int n = list.Count;
      while (n > 1)
      {
        n--;
        int k = ThreadSafeRandom.ThisThreadsRandom.Next(n + 1);
        T value = list[k];
        list[k] = list[n];
        list[n] = value;
      }
    }
  }
}

Shuffle any (I)List with an extension method based on the Fisher-Yates shuffle:

private static Random rng = new Random();  

public static void Shuffle<T>(this IList<T> list)  
{  
    int n = list.Count;  
    while (n > 1) {  
        n--;  
        int k = rng.Next(n + 1);  
        T value = list[k];  
        list[k] = list[n];  
        list[n] = value;  
    }  
}

Usage:

List<Product> products = GetProducts();
products.Shuffle();

The code above uses the much criticised System.Random method to select swap candidates. It's fast but not as random as it should be. If you need a better quality of randomness in your shuffles use the random number generator in System.Security.Cryptography like so:

using System.Security.Cryptography;
...
public static void Shuffle<T>(this IList<T> list)
{
    RNGCryptoServiceProvider provider = new RNGCryptoServiceProvider();
    int n = list.Count;
    while (n > 1)
    {
        byte[] box = new byte[1];
        do provider.GetBytes(box);
        while (!(box[0] < n * (Byte.MaxValue / n)));
        int k = (box[0] % n);
        n--;
        T value = list[k];
        list[k] = list[n];
        list[n] = value;
    }
}

A simple comparison is available at this blog (WayBack Machine).

Edit: Since writing this answer a couple years back, many people have commented or written to me, to point out the big silly flaw in my comparison. They are of course right. There's nothing wrong with System.Random if it's used in the way it was intended. In my first example above, I instantiate the rng variable inside of the Shuffle method, which is asking for trouble if the method is going to be called repeatedly. Below is a fixed, full example based on a really useful comment received today from @weston here on SO.

Program.cs:

using System;
using System.Collections.Generic;
using System.Threading;

namespace SimpleLottery
{
  class Program
  {
    private static void Main(string[] args)
    {
      var numbers = new List<int>(Enumerable.Range(1, 75));
      numbers.Shuffle();
      Console.WriteLine("The winning numbers are: {0}", string.Join(",  ", numbers.GetRange(0, 5)));
    }
  }

  public static class ThreadSafeRandom
  {
      [ThreadStatic] private static Random Local;

      public static Random ThisThreadsRandom
      {
          get { return Local ?? (Local = new Random(unchecked(Environment.TickCount * 31 + Thread.CurrentThread.ManagedThreadId))); }
      }
  }

  static class MyExtensions
  {
    public static void Shuffle<T>(this IList<T> list)
    {
      int n = list.Count;
      while (n > 1)
      {
        n--;
        int k = ThreadSafeRandom.ThisThreadsRandom.Next(n + 1);
        T value = list[k];
        list[k] = list[n];
        list[n] = value;
      }
    }
  }
}
放手` 2024-07-15 17:54:30

如果我们只需要以完全随机的顺序打乱项目(只是为了混合列表中的项目),我更喜欢这个简单而有效的代码,通过 guid 对项目进行排序...

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

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

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

If we only need to shuffle items in a completely random order (just to mix the items in a list), I prefer this simple yet effective code that orders items by guid...

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

As people have pointed out in the comments, GUIDs are not guaranteed to be random, so we should be using a real random number generator instead:

private static Random rng = new Random();
...
var shuffledcards = cards.OrderBy(_ => rng.Next()).ToList();
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文