1. ホーム
  2. c#

[解決済み] リスト<T>をランダム化する

2022-03-16 12:05:16

質問

C#で一般的なリストの順序をランダムにする最良の方法は何ですか?私は、有限の75個の数字のリストを持っていて、抽選のようなアプリケーションのために、ランダムな順序を割り当てたいと思っています。

どのように解決するのですか?

任意のシャッフル (I)List をベースにした拡張メソッドで フィッシャーイェーツシャッフル :

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 をインスタンス化していますが、このメソッドが繰り返し呼ばれることになるなら、これはトラブルのもとです。以下は、今日SOの@westonから受け取ったとても有用なコメントに基づいて、修正した完全な例です。

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