Acak Daftar <T>

855

Apa cara terbaik untuk mengacak urutan daftar generik dalam C #? Saya punya satu set terbatas dari 75 angka dalam daftar yang ingin saya berikan urutan acak, untuk menggambar mereka untuk aplikasi jenis lotre.

mirezus
sumber
3
Ada masalah terbuka untuk mengintegrasikan fungsi ini ke .NET: github.com/dotnet/corefx/issues/461
Natan
5
Anda mungkin tertarik dengan paket NuGet ini , yang berisi metode ekstensi untuk mengacak IList <T> dan IEnumerable <T> menggunakan algoritma Fisher-Yates yang disebutkan di bawah ini
ChaseMedallion
3
@ Natan mereka menutup masalah ini karena seseorang "bekerja di banyak proyek dan mengembangkan banyak perpustakaan dan tidak pernah membutuhkan metode seperti itu" yang membuat saya kesal. Sekarang kita harus menyelidiki diri kita sendiri, mencari implementasi terbaik, membuang waktu untuk hanya menemukan kembali roda.
Vitalii Isaenko
1
Apakah saya melihat ini benar? Bukan jawaban fungsional tunggal yang valid setelah 10 tahun? Mungkin kita perlu hadiah lain untuk solusi yang membahas jumlah entropi yang dibutuhkan, untuk mengacak daftar dengan 75 angka $ log2 (75!) = 364 $ dan bagaimana kita bisa mendapatkan ini. Seseorang akan perlu me-reseed bahkan RNG yang aman secara kriptografis dengan 256 bit entropi setidaknya satu kali selama kocokan fisher-yates.
Falco
1
Dan jika coder biasa tidak dapat menyelesaikan masalah ini, apakah kita semua telah memainkan 0,01% kemungkinan game solitaire yang sama selamanya?
Falco

Jawaban:

1137

Acak apa saja (I)Listdengan metode ekstensi berdasarkan shuffle Fisher-Yates :

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

Pemakaian:

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

Kode di atas menggunakan metode System.Random yang banyak dikritik untuk memilih calon swap. Ini cepat tetapi tidak acak sebagaimana mestinya. Jika Anda membutuhkan kualitas keacakan yang lebih baik dalam shuffles Anda, gunakan penghasil angka acak dalam System.Security.Cryptography seperti:

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

Perbandingan sederhana tersedia di blog ini (WayBack Machine).

Sunting: Sejak menulis jawaban ini beberapa tahun yang lalu, banyak orang berkomentar atau menulis kepada saya, untuk menunjukkan kelemahan besar yang konyol dalam perbandingan saya. Mereka tentu saja benar. Tidak ada yang salah dengan System.Random jika digunakan seperti yang dimaksudkan. Dalam contoh pertama saya di atas, saya instantiate variabel rng di dalam metode Shuffle, yang meminta masalah jika metode ini akan dipanggil berulang kali. Di bawah ini adalah contoh lengkap dan tetap berdasarkan komentar yang sangat berguna yang diterima hari ini dari @weston di sini di 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;
      }
    }
  }
}
granat
sumber
32
Bagaimana jika list.Count adalah> Byte.MaxValue? Jika n = 1000, maka 255/1000 = 0, jadi do loop akan menjadi infinite loop karena kotak [0] <0 selalu salah.
AndrewS
18
Saya ingin menunjukkan, bahwa perbandingannya cacat. Menggunakan <code> Acak baru () </code> dalam satu lingkaran adalah masalahnya, bukan keacakan <code> Acak </code> Penjelasan
Sven
9
Merupakan ide bagus untuk memberikan contoh Acak ke metode Acak daripada membuatnya di dalam seolah-olah Anda memanggil Acak banyak kali berturut-turut cepat (misalnya mengocok banyak daftar pendek), daftar semua akan dikocok dalam yang sama cara (mis. item pertama selalu dipindahkan ke posisi 3).
Mark Heath
7
Hanya membuat Random rng = new Random();sebuah staticakan memecahkan masalah di pos perbandingan. Karena setiap panggilan berikutnya akan mengikuti dari panggilan sebelumnya hasil acak terakhir.
weston
5
# 2, tidak jelas bahwa versi dengan generator Crypto berfungsi karena kisaran maksimum byte adalah 255, jadi daftar apa pun yang lebih besar dari itu tidak akan mengocok dengan benar.
Mark Sowul
336

Jika kita hanya perlu mengacak item dalam urutan yang benar-benar acak (hanya untuk mencampur item dalam daftar), saya lebih suka kode sederhana namun efektif ini yang memesan item dengan panduan ...

var shuffledcards = cards.OrderBy(a => Guid.NewGuid()).ToList();
pengguna453230
sumber
40
GUID dimaksudkan untuk menjadi unik bukan acak. Sebagian darinya berbasis mesin dan sebagian lainnya berbasis waktu dan hanya sebagian kecil yang acak. blogs.msdn.com/b/oldnewthing/archive/2008/06/27/8659071.aspx
Despertar
99
Ini adalah solusi elegan yang bagus. Jika Anda menginginkan sesuatu selain panduan untuk menghasilkan keacakan, pesan saja dengan sesuatu yang lain. Misalnya: var shuffledcards = cards.OrderBy(a => rng.Next()); compilr.com/grenade/sandbox/Program.cs
granat
20
Tolong jangan. Ini salah. "memesan secara acak" sama sekali BUKAN acak: Anda membuat bias dan, lebih buruk lagi, Anda berisiko masuk dalam loop tak terbatas
Vito De Tullio
78
@VitoDeTullio: Anda salah mengingat. Anda berisiko loop tak terbatas ketika Anda menyediakan fungsi perbandingan acak ; fungsi perbandingan diperlukan untuk menghasilkan urutan total yang konsisten . Kunci acak baik-baik saja. Saran ini salah karena panduan tidak dijamin acak , bukan karena teknik penyortiran dengan kunci acak salah.
Eric Lippert
24
@Doug: NewGuidhanya menjamin bahwa itu memberi Anda GUID unik. Itu tidak membuat jaminan tentang keacakan. Jika Anda menggunakan GUID untuk tujuan selain membuat nilai unik , Anda salah melakukannya.
Eric Lippert
120

Saya sedikit terkejut dengan semua versi kikuk dari algoritma sederhana ini di sini. Fisher-Yates (atau Knuth shuffle) agak rumit tetapi sangat kompak. Mengapa ini rumit? Karena Anda perlu memperhatikan apakah generator angka acak Anda r(a,b)mengembalikan nilai di mana binklusif atau eksklusif. Saya juga mengedit deskripsi Wikipedia sehingga orang tidak secara buta mengikuti kodesemu di sana dan membuat sulit untuk mendeteksi bug. Untuk .Net, Random.Next(a,b)mengembalikan nomor eksklusif btanpa basa-basi lagi, inilah cara penerapannya di C # /. Net:

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

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

Coba kode ini .

Shital Shah
sumber
Bukankah lebih baik mengubah rnd (i, list.Count) menjadi rnd (0, list.Count) sehingga kartu apa saja dapat ditukar?
Donat
16
@ Donat - tidak. Jika Anda melakukannya, Anda akan menambahkan bias dalam acak.
Shital Shah
2
Dengan memisahkan Tukar <T> ke metode terpisah, sepertinya Anda menyebabkan banyak alokasi T yang tidak perlu untuk temp.
Clay
2
Saya berpendapat bahwa LINQ berpotensi memperlambat kinerja pengocokan, dan itu akan menjadi alasan untuk tidak menggunakannya, terutama mengingat kesederhanaan relatif dari kode.
winglerw28
7
Kapan i = list.Count - 1, yaitu iterasi terakhir, rnd.Next(i, list.Count)akan memberikan Anda kembali. Karena itu Anda perlu i < list.Count -1sebagai kondisi loop. Nah, Anda tidak 'membutuhkannya', tetapi menghemat 1 iterasi;)
Pod
78

Metode ekstensi untuk IEnumerable:

public static IEnumerable<T> Randomize<T>(this IEnumerable<T> source)
{
    Random rnd = new Random();
    return source.OrderBy<T, int>((item) => rnd.Next());
}
Denis
sumber
3
Perhatikan bahwa ini bukan thread-safe, bahkan jika digunakan pada daftar thread-safe
BlueRaja - Danny Pflughoeft
1
bagaimana cara kita memberikan daftar <string> ke fungsi ini?
MonsterMMORPG
8
Ada dua masalah signifikan dengan algoritma ini: - OrderBymenggunakan varian QuickSort untuk mengurutkan item dengan kunci (seolah-olah acak). Kinerja QuickSort adalah O (N log N) ; sebaliknya, shuffle Fisher-Yates adalah O (N) . Untuk koleksi 75 elemen, ini mungkin bukan masalah besar, tetapi perbedaannya akan menjadi nyata untuk koleksi yang lebih besar.
John Beyer
10
... - Random.Next()dapat menghasilkan distribusi nilai pseudo-acak yang cukup, tetapi tidak menjamin bahwa nilai tersebut akan unik. Peluang kunci duplikat tumbuh (non-linear) dengan N hingga mencapai kepastian ketika N mencapai 2 ^ 32 + 1. The OrderByQuickSort adalah stabil semacam; dengan demikian, jika beberapa elemen terjadi untuk mendapatkan nilai indeks pseudo-acak yang sama, maka urutannya dalam urutan output akan sama seperti pada urutan input; dengan demikian, bias dimasukkan ke dalam "shuffle".
John Beyer
27
@ JohnBeyer: Ada jauh, masalah yang jauh lebih besar daripada sumber bias itu. Hanya ada empat miliar kemungkinan benih untuk Acak, yang jauh, jauh lebih sedikit dari jumlah kemungkinan pengocokan satu set berukuran sedang. Hanya sebagian kecil dari kemungkinan pengocokan yang dapat dihasilkan. Bias itu mengerdilkan bias karena tabrakan tidak disengaja.
Eric Lippert
16

Ide mendapatkan objek anonim dengan item dan urutan acak lalu menyusun ulang item berdasarkan urutan dan nilai pengembalian ini:

var result = items.Select(x => new { value = x, order = rnd.Next() })
            .OrderBy(x => x.order).Select(x => x.value).ToList()
Andrey Kucher
sumber
3
solusi satu liner terbaik
vipin8169
1
Anda kehilangan titik koma pada akhirnya fyi
reggaeguitar
Jika ada yang tidak yakin tentang rnd tambahkan ini sebelum kode di atas Random rnd = new Random ();
Greg Trevellick
10
    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;
    }
Adam Tegen
sumber
4
Bukankah seharusnya Anda melakukan sesuatu seperti var listCopy = list.ToList()untuk menghindari mengeluarkan semua item dari daftar yang masuk? Saya tidak benar-benar mengerti mengapa Anda ingin bermutasi dari daftar tersebut menjadi kosong.
Chris Marisic
9

EDIT The RemoveAtadalah kelemahan dalam versi sebelumnya. Solusi ini mengatasi itu.

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

Perhatikan opsional Random generator, jika implementasi kerangka dasarRandom tidak aman atau kriptografis cukup kuat untuk kebutuhan Anda, Anda dapat menyuntikkan implementasi Anda ke dalam operasi.

Implementasi yang sesuai untuk implementasi thread-safe yang secara cryptographically kuat Randomdapat ditemukan dalam jawaban ini.


Inilah sebuah ide, perluas IList dengan cara (semoga) efisien.

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

Jodrell
sumber
Lihat stackoverflow.com/questions/4412405/… . Anda harus sudah sadar.
nawfal
@nawfal lihat peningkatan implementasi saya.
Jodrell
1
hmm cukup adil. Apakah itu GetNextatau Next?
nawfal
4

Anda dapat mencapainya dengan menggunakan metode ekstensi sederhana ini

public static class IEnumerableExtensions
{

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

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

dan Anda dapat menggunakannya dengan melakukan hal berikut

// 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);
}
Shehab Fawzy
sumber
3
Saya akan menjaga Randominstance kelas di luar fungsi sebagai staticvariabel. Kalau tidak, Anda mungkin mendapatkan seed pengacakan yang sama dari timer jika dipanggil secara berurutan.
Biji Lemon
Catatan yang menarik - jika Anda membuat instance kelas Acak dengan cepat dalam satu lingkaran, katakan antara 0 ms dan 200 ms satu sama lain, maka Anda memiliki peluang yang sangat tinggi untuk mendapatkan benih pengacakan yang sama - yang kemudian menghasilkan hasil yang berulang. Namun Anda dapat menyiasatinya menggunakan Random rand = Random baru (Guid.NewGuid (). GetHashCode ()); Ini secara efektif memaksa pengacakan berasal dari Guid.NewGuid ()
Baaleos
4

Ini adalah metode shuffle pilihan saya ketika diinginkan untuk tidak memodifikasi yang asli. Ini adalah varian dari algoritma Fisher-Yates "inside-out" yang bekerja pada urutan enumerable apa pun (panjangnya sourcetidak perlu diketahui dari awal).

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

Algoritma ini juga dapat diimplementasikan dengan mengalokasikan rentang dari 0hinggalength - 1 dan melelahkan indeks secara acak dengan menukar indeks yang dipilih secara acak dengan indeks terakhir sampai semua indeks telah dipilih tepat sekali. Kode di atas menyelesaikan hal yang sama persis tetapi tanpa alokasi tambahan. Yang cukup rapi.

Berkenaan dengan Randomkelas itu adalah generator nomor tujuan umum (dan Jika saya menjalankan lotre saya akan mempertimbangkan menggunakan sesuatu yang berbeda). Itu juga bergantung pada nilai seed berdasarkan waktu secara default. Pengurangan kecil dari masalahnya adalah dengan menaburkan Randomkelas dengan RNGCryptoServiceProvideratau Anda dapat menggunakan RNGCryptoServiceProviderdalam metode yang mirip dengan ini (lihat di bawah) untuk menghasilkan nilai titik mengambang ganda acak yang dipilih secara seragam, tetapi menjalankan lotre cukup banyak membutuhkan pemahaman keacakan dan sifat dari sumber keacakan.

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

Titik menghasilkan dobel acak (antara 0 dan 1 secara eksklusif) adalah menggunakan skala untuk solusi integer. Jika Anda perlu mengambil sesuatu dari daftar berdasarkan pada double acak xyang selalu akan 0 <= x && x < 1lurus ke depan.

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

Nikmati!

John Leidegren
sumber
4

Jika Anda tidak keberatan menggunakan dua Lists, maka ini mungkin cara termudah untuk melakukannya, tetapi mungkin bukan yang paling efisien atau tidak dapat diprediksi:

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);
Xelights
sumber
3

Jika Anda memiliki nomor tetap (75), Anda bisa membuat array dengan 75 elemen, lalu menghitung daftar Anda, memindahkan elemen ke posisi acak dalam array. Anda dapat menghasilkan pemetaan nomor daftar untuk indeks array menggunakan shuffle Fisher-Yates .

dmo
sumber
3

Saya biasanya menggunakan:

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);
}
albertein
sumber
list.RemoveAt adalah operasi O (n), yang membuat implementasi ini lambat.
George Polevoy
1
    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.
pengguna7767814
sumber
0

Berikut adalah Shuffler yang efisien yang mengembalikan array byte dari nilai-nilai yang diacak. Itu tidak pernah mengocok lebih dari yang dibutuhkan. Itu dapat dimulai kembali dari tempat sebelumnya. Implementasi aktual saya (tidak diperlihatkan) adalah komponen MEF yang memungkinkan pengocok pengganti yang ditentukan pengguna.

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

`

BSalita
sumber
0

Berikut cara aman untuk melakukannya:

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());
    }
}
Christopher Stevenson
sumber
0
 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);

}
sumadd laddha
sumber
2
Selamat Datang di Stack Overflow! Harap pertimbangkan untuk menambahkan beberapa penjelasan pada jawaban Anda, daripada hanya satu blok besar kode. Tujuan kami di sini adalah untuk mendidik orang agar mereka memahami jawabannya dan dapat menerapkannya dalam situasi lain. Jika Anda mengomentari kode Anda dan menambahkan penjelasan, Anda akan membuat jawaban Anda lebih bermanfaat bukan hanya untuk orang yang mengajukan pertanyaan kali ini, tetapi juga kepada siapa pun di masa depan yang mungkin memiliki masalah yang sama.
starsplusplus
4
Sebagian besar kode ini sama sekali tidak relevan dengan pertanyaan, dan satu-satunya bagian yang bermanfaat pada dasarnya mengulangi jawaban Adam Tegen dari hampir 6 tahun yang lalu.
TC
0

Modifikasi sederhana dari jawaban yang diterima yang mengembalikan daftar baru alih-alih bekerja di tempat, dan menerima yang lebih umum IEnumerable<T>seperti metode Linq lainnya.

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;
}
Luar biasa
sumber
-5

Posting lama pasti, tapi saya hanya menggunakan GUID.

Items = Items.OrderBy(o => Guid.NewGuid().ToString()).ToList();

GUID selalu unik, dan karena itu dibuat ulang setiap kali hasilnya berubah setiap kali.

DavidMc
sumber
Ringkas, tetapi apakah Anda memiliki referensi tentang pengurutan newGuids berturut-turut menjadi acak berkualitas tinggi? Beberapa versi quid / uuid memiliki prangko waktu dan bagian non-acak lainnya.
Johan Lundberg
8
Jawaban ini sudah diberikan, dan lebih buruk lagi dirancang untuk keunikan bukan keacakan.
Alex Angas
-7

Pendekatan yang sangat sederhana untuk masalah semacam ini adalah dengan menggunakan sejumlah swap elemen acak dalam daftar.

Dalam pseudo-code ini akan terlihat seperti ini:

do 
    r1 = randomPositionInList()
    r2 = randomPositionInList()
    swap elements at index r1 and index r2 
for a certain number of times
Aleris
sumber
1
Satu masalah dengan pendekatan ini adalah mengetahui kapan harus berhenti. Ia juga memiliki kecenderungan untuk membesar-besarkan bias apa pun dalam generator bilangan pseudo-acak.
Mark Bessey
3
Iya. Sangat tidak efisien. Tidak ada alasan untuk menggunakan pendekatan seperti ini ketika ada pendekatan yang lebih baik dan lebih cepat yang sesederhana itu.
PeterAllenWebb
1
tidak terlalu efisien atau efektif ... Menjalankannya N kali kemungkinan akan meninggalkan banyak elemen di posisi semula.
NSjonas