Apakah menggunakan Random dan Order Dengan algoritma shuffle yang baik?

164

Saya telah membaca sebuah artikel tentang berbagai algoritma acak di Coding Horror . Saya telah melihat bahwa di suatu tempat orang telah melakukan ini untuk mengacak daftar:

var r = new Random();
var shuffled = ordered.OrderBy(x => r.Next());

Apakah ini algoritma pengocokan yang baik? Bagaimana cara kerjanya? Apakah ini cara yang dapat diterima untuk melakukan ini?

Svish
sumber

Jawaban:

205

Ini bukan cara pengocokan yang saya sukai, sebagian besar dengan alasan itu adalah O (n log n) tanpa alasan yang baik ketika mudah untuk menerapkan pengocokan O (n). Kode dalam pertanyaan "berfungsi" pada dasarnya memberikan nomor acak (semoga unik!) Untuk setiap elemen, lalu memesan elemen sesuai dengan nomor itu.

Saya lebih suka varian Durstenfield dari shuffle Fisher-Yates yang menukar elemen.

Menerapkan Shufflemetode ekstensi sederhana pada dasarnya akan terdiri dari panggilan ToListatau ToArrayinput kemudian menggunakan implementasi Fisher-Yates yang sudah ada. (Lewati Randomsebagai parameter untuk membuat hidup secara umum lebih baik.) Ada banyak implementasi di sekitar ... Saya mungkin punya satu di jawaban di suatu tempat.

Hal yang menyenangkan tentang metode perluasan semacam itu adalah bahwa akan sangat jelas bagi pembaca apa yang sebenarnya Anda coba lakukan.

EDIT: Ini implementasi sederhana (tanpa pengecekan kesalahan!):

public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source, Random rng)
{
    T[] elements = source.ToArray();
    // Note i > 0 to avoid final pointless iteration
    for (int i = elements.Length-1; i > 0; i--)
    {
        // Swap element "i" with a random earlier element it (or itself)
        int swapIndex = rng.Next(i + 1);
        T tmp = elements[i];
        elements[i] = elements[swapIndex];
        elements[swapIndex] = tmp;
    }
    // Lazily yield (avoiding aliasing issues etc)
    foreach (T element in elements)
    {
        yield return element;
    }
}

Sunting: Komentar tentang kinerja di bawah mengingatkan saya bahwa kami dapat benar-benar mengembalikan elemen saat mengocoknya:

public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source, Random rng)
{
    T[] elements = source.ToArray();
    for (int i = elements.Length - 1; i >= 0; i--)
    {
        // Swap element "i" with a random earlier element it (or itself)
        // ... except we don't really need to swap it fully, as we can
        // return it immediately, and afterwards it's irrelevant.
        int swapIndex = rng.Next(i + 1);
        yield return elements[swapIndex];
        elements[swapIndex] = elements[i];
    }
}

Ini sekarang hanya akan melakukan pekerjaan sebanyak yang dibutuhkan.

Perhatikan bahwa dalam kedua kasus, Anda harus berhati-hati tentang contoh yang RandomAnda gunakan sebagai:

  • Membuat dua contoh secara Randomkasar pada waktu yang sama akan menghasilkan urutan angka acak yang sama (bila digunakan dengan cara yang sama)
  • Random tidak aman untuk thread.

Saya punya artikelRandom yang membahas lebih detail tentang masalah ini dan memberikan solusi.

Jon Skeet
sumber
5
Baik, implementasi untuk hal-hal kecil, tetapi penting, seperti ini yang saya katakan selalu menyenangkan ditemukan di sini di StackOverflow. Jadi ya tolong, jika Anda ingin =)
Svish
9
Jon - penjelasan Anda tentang Fisher-Yates setara dengan implementasi yang diberikan dalam pertanyaan (versi naif). Durstenfeld / Knuth mencapai O (n) bukan dengan tugas, tetapi dengan seleksi dari set dan swap yang menurun. Dengan cara ini nomor acak yang dipilih dapat diulang dan algoritma hanya membutuhkan O (n).
tvanfosson
8
Anda mungkin muak mendengar dari saya tentang hal ini, tetapi saya mengalami sedikit masalah dalam tes unit saya yang mungkin ingin Anda ketahui. Ada kekhasan dengan ElementAt yang membuatnya memohon ekstensi setiap kali, memberikan hasil yang tidak dapat diandalkan. Dalam tes saya, saya mewujudkan hasil sebelum memeriksa untuk menghindari ini.
tvanfosson
3
@vanfosson: Tidak sakit sama sekali :) Tapi ya, penelepon harus menyadari bahwa itu dievaluasi dengan malas.
Jon Skeet
4
Agak terlambat, tetapi harap dicatat source.ToArray();Anda harus memiliki using System.Linq;file yang sama. Jika tidak, Anda mendapatkan kesalahan ini:'System.Collections.Generic.IEnumerable<T>' does not contain a definition for 'ToArray' and no extension method 'ToArray' accepting a first argument of type 'System.Collections.Generic.IEnumerable<T>' could be found (are you missing a using directive or an assembly reference?)
Powerlord
70

Ini berdasarkan jawaban Jon Skeet .

Dalam jawaban itu, array dikocok, lalu dikembalikan menggunakan yield. Hasil akhirnya adalah array disimpan dalam memori selama durasi foreach, serta objek yang diperlukan untuk iterasi, namun biayanya semua pada awalnya - hasilnya pada dasarnya adalah loop kosong.

Algoritma ini banyak digunakan dalam permainan, di mana tiga item pertama diambil, dan yang lainnya hanya akan dibutuhkan nanti jika sama sekali. Saran saya adalah yieldnomor segera setelah mereka ditukar. Ini akan mengurangi biaya awal, sambil menjaga biaya iterasi pada O (1) (pada dasarnya 5 operasi per iterasi). Total biaya akan tetap sama, tetapi pengocokan itu sendiri akan lebih cepat. Dalam kasus-kasus di mana ini disebut collection.Shuffle().ToArray()secara teoritis tidak akan membuat perbedaan, tetapi dalam kasus-kasus penggunaan yang disebutkan di atas akan mempercepat start-up. Selain itu, ini akan membuat algoritme berguna untuk kasus di mana Anda hanya perlu beberapa item unik. Misalnya, jika Anda perlu mengeluarkan tiga kartu dari setumpuk 52, Anda dapat memanggil deck.Shuffle().Take(3)dan hanya tiga swap yang akan terjadi (meskipun seluruh array harus disalin terlebih dahulu).

public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source, Random rng)
{
    T[] elements = source.ToArray();
    // Note i > 0 to avoid final pointless iteration
    for (int i = elements.Length - 1; i > 0; i--)
    {
        // Swap element "i" with a random earlier element it (or itself)
        int swapIndex = rng.Next(i + 1);
        yield return elements[swapIndex];
        elements[swapIndex] = elements[i];
        // we don't actually perform the swap, we can forget about the
        // swapped element because we already returned it.
    }

    // there is one item remaining that was not returned - we return it now
    yield return elements[0]; 
}
konfigurator
sumber
Aduh! Ini kemungkinan tidak akan mengembalikan semua item di sumber. Anda tidak dapat mengandalkan nomor acak yang unik untuk iterasi N.
P Ayah
2
Pintar! (Dan aku benci barang 15 karakter ini ...)
Svish
@ P Ayah: Hah? Mau menguraikan?
Svish
1
Atau Anda bisa mengganti> 0 dengan> = 0 dan tidak harus (meskipun, hit RNG tambahan ditambah tugas yang berlebihan)
FryGuy
4
Biaya awal adalah O (N) sebagai biaya sumber.ToArray ();
Dave Hillier
8

Mulai dari kutipan Skeet ini:

Ini bukan cara pengocokan yang saya sukai, sebagian besar dengan alasan itu adalah O (n log n) tanpa alasan yang baik ketika mudah untuk menerapkan pengocokan O (n). Kode dalam pertanyaan "berfungsi" pada dasarnya memberikan nomor acak ( semoga unik! ) Untuk setiap elemen, lalu memesan elemen sesuai dengan nomor itu.

Saya akan menjelaskan sedikit alasan untuk semoga unik!

Sekarang, dari Enumerable.OrderBy :

Metode ini melakukan semacam stabil; yaitu, jika kunci dua elemen sama, urutan elemen dipertahankan

Ini sangat penting! Apa yang terjadi jika dua elemen "menerima" nomor acak yang sama? Itu terjadi bahwa mereka tetap dalam urutan yang sama mereka berada di array. Sekarang, bagaimana kemungkinan ini terjadi? Sulit untuk menghitung dengan tepat, tetapi ada Masalah Ulang Tahun yang justru merupakan masalah ini.

Sekarang, benarkah? Apakah itu benar

Seperti biasa, ketika ragu, tulis beberapa baris program: http://pastebin.com/5CDnUxPG

Blok kecil kode ini mengocok array 3 elemen beberapa kali menggunakan algoritma Fisher-Yates yang dilakukan mundur, algoritma Fisher-Yates dilakukan ke depan (pada halaman wiki ada dua algoritma pseudo-code ... Mereka menghasilkan setara hasil, tetapi satu dilakukan dari elemen pertama ke elemen terakhir, sedangkan yang lain dilakukan dari elemen terakhir ke elemen pertama), algoritma salah naif dari http://blog.codinghorror.com/the-danger-of-naivete/ dan menggunakan .OrderBy(x => r.Next())dan .OrderBy(x => r.Next(someValue)).

Sekarang, Random.Next adalah

Bilangan bulat bertanda 32-bit yang lebih besar dari atau sama dengan 0 dan kurang dari MaxValue.

jadi itu setara dengan

OrderBy(x => r.Next(int.MaxValue))

Untuk menguji apakah masalah ini ada, kita dapat memperbesar array (sesuatu yang sangat lambat) atau cukup mengurangi nilai maksimum dari generator angka acak ( int.MaxValuebukan nomor "khusus" ... Ini hanyalah angka yang sangat besar). Pada akhirnya, jika algoritma tidak bias oleh stableness dariOrderBy , maka rentang nilai apa pun harus memberikan hasil yang sama.

Program kemudian menguji beberapa nilai, dalam kisaran 1 ... 4096. Melihat hasilnya, cukup jelas bahwa untuk nilai rendah (<128), algoritmenya sangat bias (4-8%). Dengan 3 nilai yang Anda butuhkan setidaknya r.Next(1024). Jika Anda membuat array lebih besar (4 atau 5), maka itu pun r.Next(1024)tidak cukup. Saya bukan ahli dalam pengocokan dan matematika, tapi saya pikir untuk setiap bit ekstra panjang array, Anda memerlukan 2 bit ekstra dari nilai maksimum (karena paradoks ulang tahun terhubung ke sqrt (numvalues)), jadi bahwa jika nilai maksimum adalah 2 ^ 31, saya akan mengatakan bahwa Anda harus dapat mengurutkan array hingga 2 ^ 12/2 ^ 13 bit (4096-8192 elemen)

xanatos
sumber
Dinyatakan dengan baik, dan menampilkan dengan sempurna masalah dengan pertanyaan aslinya. Ini harus digabung dengan jawaban Jon.
TheSoftwareJedi
6

Ini mungkin ok untuk sebagian besar tujuan, dan hampir selalu menghasilkan distribusi yang benar-benar acak (kecuali ketika Random.Next () menghasilkan dua bilangan bulat acak identik).

Ia bekerja dengan menetapkan setiap elemen dari seri bilangan bulat acak, lalu memesan urutannya dengan bilangan bulat ini.

Ini benar-benar dapat diterima untuk 99,9% dari aplikasi (kecuali jika Anda benar-benar harus menangani kasus tepi di atas). Selain itu, keberatan skeet terhadap runtime-nya valid, jadi jika Anda mengacak daftar panjang, Anda mungkin tidak ingin menggunakannya.

ripper234
sumber
4

Ini telah muncul berkali-kali sebelumnya. Cari Fisher-Yates di StackOverflow.

Berikut adalah contoh kode C # yang saya tulis untuk algoritma ini. Anda dapat membuat parameter pada beberapa jenis lain, jika Anda mau.

static public class FisherYates
{
        //      Based on Java code from wikipedia:
        //      http://en.wikipedia.org/wiki/Fisher-Yates_shuffle
        static public void Shuffle(int[] deck)
        {
                Random r = new Random();
                for (int n = deck.Length - 1; n > 0; --n)
                {
                        int k = r.Next(n+1);
                        int temp = deck[n];
                        deck[n] = deck[k];
                        deck[k] = temp;
                }
        }
}
hughdbrown
sumber
2
Anda seharusnya tidak menggunakan Randomvariabel statis seperti ini - Randomtidak aman untuk thread. Lihat csharpindepth.com/Articles/Chapter12/Random.aspx
Jon Skeet
@ Jon Skeet: tentu, itu argumen yang sah. OTOH, OP bertanya tentang suatu algoritma yang benar-benar salah padahal ini benar (selain case-shuffling card-multithreaded use-case).
hughdbrown
1
Itu hanya berarti ini "kurang salah" dari pendekatan OP. Bukan berarti itu kode yang harus digunakan tanpa pemahaman bahwa itu tidak dapat digunakan dengan aman dalam konteks multi-utas ... yang merupakan sesuatu yang tidak Anda sebutkan. Ada harapan yang masuk akal bahwa anggota statis dapat digunakan dengan aman dari banyak utas.
Jon Skeet
@ Jon Skeet: Tentu, saya bisa mengubahnya. Selesai Saya cenderung berpikir bahwa kembali ke pertanyaan menjawab tiga setengah tahun yang lalu dan berkata, "Itu tidak benar karena tidak menangani kasus penggunaan multithreaded" ketika OP tidak pernah bertanya tentang sesuatu yang lebih dari algoritma yang berlebihan. Tinjau jawaban saya selama bertahun-tahun. Seringkali saya telah memberikan balasan OP yang melampaui persyaratan yang dinyatakan. Saya telah dikritik karena itu. Saya tidak akan mengharapkan OP untuk mendapatkan jawaban yang sesuai dengan semua kegunaan yang mungkin.
hughdbrown
Saya hanya mengunjungi jawaban ini sama sekali karena orang lain menunjuk saya padanya di obrolan. Meskipun OP tidak secara khusus menyebutkan threading, saya pikir itu layak disebut ketika metode statis tidak aman-thread, karena itu tidak biasa dan membuat kode tidak cocok untuk banyak situasi tanpa modifikasi. Kode baru Anda aman-utas - tetapi masih tidak ideal seperti jika Anda memanggilnya dari beberapa utas pada "kira-kira" pada saat yang sama untuk mengacak dua koleksi dengan ukuran yang sama, pengocokan akan sama. Pada dasarnya, Randomadalah rasa sakit untuk digunakan, seperti disebutkan dalam artikel saya.
Jon Skeet
3

Sepertinya algoritma pengocokan yang baik, jika Anda tidak terlalu khawatir dengan kinerja. Satu-satunya masalah yang saya tunjukkan adalah bahwa perilakunya tidak dapat dikontrol, jadi Anda mungkin kesulitan mengujinya.

Salah satu opsi yang mungkin adalah memiliki seed untuk dikirimkan sebagai parameter ke generator angka acak (atau generator acak sebagai parameter), sehingga Anda dapat memiliki lebih banyak kontrol dan mengujinya dengan lebih mudah.

Samuel Carrijo
sumber
3

Saya menemukan jawaban Jon Skeet sepenuhnya memuaskan, tetapi robo-scanner klien saya akan melaporkan Randomkesalahan keamanan. Jadi saya menukarnya System.Security.Cryptography.RNGCryptoServiceProvider. Sebagai bonus, itu memperbaiki masalah keamanan thread yang disebutkan. Di sisi lain, RNGCryptoServiceProvidertelah diukur 300x lebih lambat daripada menggunakanRandom .

Pemakaian:

using (var rng = new RNGCryptoServiceProvider())
{
    var data = new byte[4];
    yourCollection = yourCollection.Shuffle(rng, data);
}

Metode:

/// <summary>
/// Shuffles the elements of a sequence randomly.
/// </summary>
/// <param name="source">A sequence of values to shuffle.</param>
/// <param name="rng">An instance of a random number generator.</param>
/// <param name="data">A placeholder to generate random bytes into.</param>
/// <returns>A sequence whose elements are shuffled randomly.</returns>
public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source, RNGCryptoServiceProvider rng, byte[] data)
{
    var elements = source.ToArray();
    for (int i = elements.Length - 1; i >= 0; i--)
    {
        rng.GetBytes(data);
        var swapIndex = BitConverter.ToUInt32(data, 0) % (i + 1);
        yield return elements[swapIndex];
        elements[swapIndex] = elements[i];
    }
}
frattaro
sumber
3

Mencari algoritma? Anda bisa menggunakan ShuffleListkelas saya :

class ShuffleList<T> : List<T>
{
    public void Shuffle()
    {
        Random random = new Random();
        for (int count = Count; count > 0; count--)
        {
            int i = random.Next(count);
            Add(this[i]);
            RemoveAt(i);
        }
    }
}

Lalu, gunakan seperti ini:

ShuffleList<int> list = new ShuffleList<int>();
// Add elements to your list.
list.Shuffle();

Bagaimana cara kerjanya?

Mari kita ambil daftar awal yang diurutkan dari 5 bilangan bulat pertama: { 0, 1, 2, 3, 4 } .

Metode ini dimulai dengan menghitung nubmer elemen dan menyebutnya count. Kemudian, dengan countmenurun pada setiap langkah, dibutuhkan angka acak antara 0dancount dan memindahkannya ke akhir daftar.

Dalam contoh langkah demi langkah berikut, item yang dapat dipindahkan adalah miring , item yang dipilih dicetak tebal :

0 1 2 3 4
0 1 2 3 4
0 1 2 4 3
0 1 2 4 3
1 2 4 3 0
1 2 4 3 0
1 2 3 0 4
1 2 3 0 4
2 3 0 4 1
2 3 0 4 1
3 0 4 1 2

SteeveDroz
sumber
Itu bukan O (n). RemoveAt saja adalah O (n).
paparazzo
Hmm, sepertinya kamu benar, salahku! Saya akan menghapus bagian itu.
SteeveDroz
1

Algoritma ini mengocok dengan menghasilkan nilai acak baru untuk setiap nilai dalam daftar, lalu memesan daftar dengan nilai acak tersebut. Anggap saja sebagai menambahkan kolom baru ke tabel di memori, lalu mengisinya dengan GUID, lalu mengurutkan berdasarkan kolom itu. Sepertinya cara yang efisien bagi saya (terutama dengan gula lambda!)

Dave Swersky
sumber
1

Sedikit tidak berhubungan, tetapi di sini adalah metode yang menarik (yang meskipun benar-benar berlebihan, telah BENAR-BENAR diterapkan) untuk pembuatan gulungan dadu yang benar-benar acak!

Dice-O-Matic

Alasan saya memposting ini di sini, adalah bahwa ia membuat beberapa poin menarik tentang bagaimana para penggunanya bereaksi terhadap gagasan menggunakan algoritma untuk mengocok, lebih dari dadu yang sebenarnya. Tentu saja, di dunia nyata, solusi semacam itu hanya untuk ujung spektrum yang sangat ekstrem di mana keacakan memiliki dampak yang sangat besar dan mungkin dampaknya memengaruhi uang;).

Irfy
sumber
1

Saya akan mengatakan bahwa banyak jawaban di sini seperti "Algoritma ini mengocok dengan menghasilkan nilai acak baru untuk setiap nilai dalam daftar, kemudian memesan daftar dengan nilai-nilai acak" mungkin sangat salah!

Saya akan berpikir bahwa ini TIDAK memberikan nilai acak untuk setiap elemen koleksi sumber. Sebagai gantinya mungkin ada semacam algoritma yang berjalan seperti Quicksort yang akan memanggil fungsi-perbandingan kira-kira n log n kali. Beberapa algortihm benar-benar mengharapkan fungsi perbandingan ini stabil dan selalu mengembalikan hasil yang sama!

Mungkinkah IEnumerableSorter memanggil fungsi bandingkan untuk setiap langkah algoritma misalnya quicksort dan setiap kali memanggil fungsi x => r.Next()untuk kedua parameter tanpa menyimpannya!

Dalam hal ini Anda mungkin benar-benar mengacaukan algoritma pengurutan dan membuatnya jauh lebih buruk daripada harapan yang dibangun oleh algoritma tersebut. Tentu saja, pada akhirnya akan menjadi stabil dan mengembalikan sesuatu.

Saya mungkin memeriksanya nanti dengan meletakkan output debugging di dalam fungsi "Next" baru jadi lihat apa yang terjadi. Di Reflector saya tidak bisa segera mengetahui cara kerjanya.

Kristen
sumber
1
Bukan itu masalahnya: menimpa internal membatalkan ComputeKeys (elemen TElement [], int count); Tipe Deklarasi: System.Linq.EnumerableSorter <TElement, TKey> Majelis: System.Core, Versi = 3.5.0.0 Fungsi ini menciptakan array pertama dengan semua tombol yang menghabiskan memori, sebelum quicksort mengurutkannya
Christian
Itu bagus untuk diketahui - masih hanya detail implementasi, yang bisa berubah di versi mendatang!
Blorgbeard keluar
-5

Waktu mulai berjalan pada kode dengan menghapus semua utas dan cache setiap pengujian baru,

Kode pertama yang gagal. Ini berjalan pada LINQPad. Jika Anda mengikuti untuk menguji kode ini.

Stopwatch st = new Stopwatch();
st.Start();
var r = new Random();
List<string[]> list = new List<string[]>();
list.Add(new String[] {"1","X"});
list.Add(new String[] {"2","A"});
list.Add(new String[] {"3","B"});
list.Add(new String[] {"4","C"});
list.Add(new String[] {"5","D"});
list.Add(new String[] {"6","E"});

//list.OrderBy (l => r.Next()).Dump();
list.OrderBy (l => Guid.NewGuid()).Dump();
st.Stop();
Console.WriteLine(st.Elapsed.TotalMilliseconds);

list.OrderBy (x => r.Next ()) menggunakan 38,6528 ms

list.OrderBy (x => Guid.NewGuid ()) menggunakan 36,7634 ms (Disarankan dari MSDN.)

setelah kedua kalinya keduanya digunakan dalam waktu yang sama.

EDIT: TEST CODE pada Intel Core i7 [email protected], Ram 8 GB DDR3 @ 1600, HDD SATA 5200 rpm dengan [Data: www.dropbox.com/s/pbtmh5s9lw285kp/data]

using System;
using System.Runtime;
using System.Diagnostics;
using System.IO;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Threading;

namespace Algorithm
{
    class Program
    {
        public static void Main(string[] args)
        {
            try {
                int i = 0;
                int limit = 10;
                var result = GetTestRandomSort(limit);
                foreach (var element in result) {
                    Console.WriteLine();
                    Console.WriteLine("time {0}: {1} ms", ++i, element);
                }
            } catch (Exception e) {
                Console.WriteLine(e.Message);
            } finally {
                Console.Write("Press any key to continue . . . ");
                Console.ReadKey(true);
            }
        }

        public static IEnumerable<double> GetTestRandomSort(int limit)
        {
            for (int i = 0; i < 5; i++) {
                string path = null, temp = null;
                Stopwatch st = null;
                StreamReader sr = null;
                int? count = null;
                List<string> list = null;
                Random r = null;

                GC.Collect();
                GC.WaitForPendingFinalizers();
                Thread.Sleep(5000);

                st = Stopwatch.StartNew();
                #region Import Input Data
                path = Environment.CurrentDirectory + "\\data";
                list = new List<string>();
                sr = new StreamReader(path);
                count = 0;
                while (count < limit && (temp = sr.ReadLine()) != null) {
//                  Console.WriteLine(temp);
                    list.Add(temp);
                    count++;
                }
                sr.Close();
                #endregion

//              Console.WriteLine("--------------Random--------------");
//              #region Sort by Random with OrderBy(random.Next())
//              r = new Random();
//              list = list.OrderBy(l => r.Next()).ToList();
//              #endregion

//              #region Sort by Random with OrderBy(Guid)
//              list = list.OrderBy(l => Guid.NewGuid()).ToList();
//              #endregion

//              #region Sort by Random with Parallel and OrderBy(random.Next())
//              r = new Random();
//              list = list.AsParallel().OrderBy(l => r.Next()).ToList();
//              #endregion

//              #region Sort by Random with Parallel OrderBy(Guid)
//              list = list.AsParallel().OrderBy(l => Guid.NewGuid()).ToList();
//              #endregion

//              #region Sort by Random with User-Defined Shuffle Method
//              r = new Random();
//              list = list.Shuffle(r).ToList();
//              #endregion

//              #region Sort by Random with Parallel User-Defined Shuffle Method
//              r = new Random();
//              list = list.AsParallel().Shuffle(r).ToList();
//              #endregion

                // Result
//              
                st.Stop();
                yield return st.Elapsed.TotalMilliseconds;
                foreach (var element in list) {
                Console.WriteLine(element);
            }
            }

        }
    }
}

Deskripsi Hasil: https://www.dropbox.com/s/9dw9wl259dfs04g/ResultDescription.PNG
Hasil Statistik: https://www.dropbox.com/s/ewq5ybtsvesme4d/ResultStat.PNG

Kesimpulan:
Asumsikan: LINQ OrderBy (r.Next ()) dan OrderBy (Guid.NewGuid ()) tidak lebih buruk daripada Metode Shuffle Buatan Pengguna dalam Solusi Pertama.

Jawaban: Mereka kontradiksi.

GMzo
sumber
1
Pilihan kedua tidak benar , dan karena itu kinerjanya tidak relevan . Ini juga masih belum menjawab pertanyaan apakah pemesanan dengan nomor acak dapat diterima, efisien, atau cara kerjanya. Solusi pertama juga memiliki masalah kebenaran, tapi mereka tidak sebagai masalah besar.
Servy
Maaf, saya ingin tahu parameter apa yang lebih baik dari Quicksort of Linq OrderBy? Saya perlu menguji kinerja. Namun, saya pikir tipe int hanya memiliki kecepatan yang lebih baik daripada string Guid tetapi tidak. Saya mengerti mengapa MSDN direkomendasikan. Solusi yang diedit solusi pertama sama dengan OrderBy dengan instance acak.
GMzo
Apa gunanya mengukur kinerja kode yang tidak menyelesaikan masalah? Kinerja hanyalah pertimbangan untuk membuat antara dua solusi yang sama-sama berfungsi . Ketika Anda memiliki solusi yang berfungsi, maka Anda dapat mulai membandingkannya.
Servy
Saya harus punya waktu untuk menguji lebih banyak data maka jika sudah selesai, saya berjanji untuk memposting lagi. Asumsikan: Saya pikir Linq OrderBy tidak lebih buruk dari solusi pertama. Opini: Mudah digunakan dan dimengerti.
GMzo
Ini terasa kurang efisien daripada algoritma pengocokan sederhana yang sangat sederhana, tetapi sekali lagi, kinerjanya tidak relevan . Mereka tidak mengacak data dengan andal, selain menjadi kurang performan.
Servy