Việc sử dụng Random và OrderBy có phải là một thuật toán xáo trộn tốt không?


164

Tôi đã đọc một bài viết về các thuật toán xáo trộn khác nhau tại Coding H khiếp sợ . Tôi đã thấy rằng một nơi nào đó mọi người đã làm điều này để xáo trộn một danh sách:

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

Đây có phải là một thuật toán xáo trộn tốt? Làm thế nào nó hoạt động chính xác? Đây có phải là một cách chấp nhận được để làm điều này?

Câu trả lời:


205

Đó không phải là một cách xáo trộn mà tôi thích, chủ yếu là với lý do đó là O (n log n) không có lý do chính đáng khi dễ dàng thực hiện xáo trộn O (n). Mã trong câu hỏi "hoạt động" bằng cách cơ bản đưa ra một số ngẫu nhiên (hy vọng là duy nhất!) Cho mỗi phần tử, sau đó sắp xếp các phần tử theo số đó.

Tôi thích biến thể của Durstenfield trong shuffle Fisher-Yates , nơi hoán đổi các yếu tố.

Việc triển khai một Shufflephương thức mở rộng đơn giản về cơ bản sẽ bao gồm gọi điện thoại ToListhoặc ToArraytrên đầu vào sau đó sử dụng triển khai Fisher-Yates hiện có. (Truyền vào Randomnhư một tham số để làm cho cuộc sống thường đẹp hơn.) Có rất nhiều triển khai xung quanh ... Có lẽ tôi đã có một câu trả lời ở đâu đó.

Điều hay ho về một phương pháp mở rộng như vậy là nó sẽ rất rõ ràng cho người đọc những gì bạn đang thực sự cố gắng làm.

EDIT: Đây là một triển khai đơn giản (không kiểm tra lỗi!):

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

EDIT: Nhận xét về hiệu suất bên dưới nhắc nhở tôi rằng chúng tôi thực sự có thể trả lại các yếu tố khi chúng tôi trộn chúng:

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

Điều này bây giờ sẽ chỉ làm nhiều công việc cần thiết.

Lưu ý rằng trong cả hai trường hợp, bạn cần cẩn thận về trường hợp Randombạn sử dụng như:

  • Tạo hai trường hợp Randomgần như cùng một lúc sẽ mang lại cùng một chuỗi các số ngẫu nhiên (khi được sử dụng theo cùng một cách)
  • Random không phải là chủ đề an toàn.

Tôi có một bài viết vềRandom chi tiết hơn về các vấn đề này và cung cấp giải pháp.


5
Chà, những triển khai cho nhỏ, nhưng quan trọng, những thứ như thế này tôi muốn nói là luôn luôn tốt để tìm thấy ở đây trên StackOverflow. Vì vậy, xin vui lòng, nếu bạn muốn =)
Svish

9
Jon - lời giải thích của bạn về Fisher-Yates tương đương với việc thực hiện trong câu hỏi (phiên bản ngây thơ). Durstenfeld / Knuth đạt được O (n) không phải bằng cách gán, mà bằng cách chọn từ một tập giảm và hoán đổi. Bằng cách này, số ngẫu nhiên được chọn có thể lặp lại và thuật toán chỉ mất O (n).
tvanfosson

8
Có lẽ bạn đang phát ốm khi nghe tôi nói về điều này, nhưng tôi gặp phải một vấn đề nhỏ trong các bài kiểm tra đơn vị của tôi mà bạn có thể muốn biết. Có một sự giải thích với ElementAt khiến nó gọi tiện ích mở rộng mỗi lần, cho kết quả không đáng tin cậy. Trong các thử nghiệm của tôi, tôi cụ thể hóa kết quả trước khi kiểm tra để tránh điều này.
tvanfosson

3
@tvanfosson: Hoàn toàn không bị bệnh :) Nhưng vâng, người gọi nên biết rằng nó được đánh giá một cách lười biếng.
Jon Skeet

4
Một chút muộn, nhưng xin lưu ý source.ToArray();yêu cầu bạn phải có using System.Linq;trong cùng một tập tin. Nếu bạn không, bạn sẽ gặp lỗi này:'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

Điều này dựa trên câu trả lời của Jon Skeet .

Trong câu trả lời đó, mảng được xáo trộn, sau đó trả về sử dụng yield. Kết quả cuối cùng là mảng được lưu giữ trong bộ nhớ trong suốt thời gian tìm kiếm, cũng như các đối tượng cần thiết cho việc lặp lại, và chi phí chỉ là lúc ban đầu - năng suất về cơ bản là một vòng lặp trống.

Thuật toán này được sử dụng rất nhiều trong các trò chơi, trong đó ba mục đầu tiên được chọn và những mục khác sẽ chỉ cần sau này nếu có. Đề nghị của tôi là yieldcác con số ngay khi chúng được hoán đổi. Điều này sẽ giảm chi phí khởi động, trong khi vẫn giữ chi phí lặp ở mức O (1) (về cơ bản là 5 thao tác trên mỗi lần lặp). Tổng chi phí sẽ giữ nguyên, nhưng việc xáo trộn sẽ nhanh hơn. Trong trường hợp điều này được gọi là collection.Shuffle().ToArray()về mặt lý thuyết sẽ không có sự khác biệt, nhưng trong các trường hợp sử dụng đã nói ở trên, nó sẽ tăng tốc độ khởi động. Ngoài ra, điều này sẽ làm cho thuật toán trở nên hữu ích cho các trường hợp bạn chỉ cần một vài mục duy nhất. Ví dụ: nếu bạn cần rút ba thẻ từ bộ bài 52, bạn có thể gọi deck.Shuffle().Take(3)và chỉ có ba lần hoán đổi (mặc dù toàn bộ mảng sẽ phải được sao chép trước).

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

Ôi! Điều này có thể sẽ không trả lại tất cả các mục trong nguồn. Bạn không thể dựa vào một số ngẫu nhiên là duy nhất cho N lần lặp.
P Daddy

2
Tài giỏi! (Và tôi ghét 15 thứ nhân vật này ...)
Svish

@P Bố ơi? Quan tâm đến công phu?
Svish

1
Hoặc bạn có thể thay thế> 0 bằng> = 0 và không phải (mặc dù, một lần truy cập RNG bổ sung cộng với chỉ định dự phòng)
FryGuy

4
Chi phí khởi động là O (N) là chi phí của nguồn.ToArray ();
Dave Hillier

8

Bắt đầu từ trích dẫn này của Skeet:

Đó không phải là một cách xáo trộn mà tôi thích, chủ yếu là với lý do đó là O (n log n) không có lý do chính đáng khi dễ dàng thực hiện xáo trộn O (n). Mã trong câu hỏi "hoạt động" bằng cách cơ bản đưa ra một số ngẫu nhiên ( hy vọng là duy nhất! ) Cho mỗi phần tử, sau đó sắp xếp các phần tử theo số đó.

Tôi sẽ tiếp tục giải thích một chút về lý do cho sự độc đáo hy vọng!

Bây giờ, từ Enumerable.OrderBy :

Phương pháp này thực hiện một loại ổn định; nghĩa là, nếu các khóa của hai phần tử bằng nhau, thứ tự của các phần tử được giữ nguyên

Cái này rất quan trọng! Điều gì xảy ra nếu hai phần tử "nhận" cùng một số ngẫu nhiên? Nó xảy ra rằng họ vẫn theo thứ tự như họ đang ở trong mảng. Bây giờ, khả năng cho điều này xảy ra là gì? Thật khó để tính toán chính xác, nhưng có vấn đề sinh nhật chính xác là vấn đề này.

Bây giờ, nó có thật không? Có đúng không

Như mọi khi, khi nghi ngờ, hãy viết một số dòng chương trình: http://pastebin.com/5CDnUxPG

Khối mã nhỏ này xáo trộn một mảng gồm 3 phần tử một số lần nhất định bằng thuật toán Fisher-Yates được thực hiện ngược, thuật toán Fisher-Yates được thực hiện về phía trước (trong trang wiki có hai thuật toán mã giả ... Chúng tạo ra tương đương kết quả, nhưng một kết quả được thực hiện từ phần tử đầu tiên đến phần tử cuối cùng, trong khi phần tử kia được thực hiện từ phần tử cuối cùng đến phần tử đầu tiên), thuật toán sai ngây thơ của http://blog.codinghorror.com/the-danger-of-naivete/ và sử dụng .OrderBy(x => r.Next()).OrderBy(x => r.Next(someValue)).

Bây giờ, Random. Tiếp theo

Số nguyên có chữ ký 32 bit lớn hơn hoặc bằng 0 và nhỏ hơn MaxValue.

vậy nó tương đương với

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

Để kiểm tra xem sự cố này có tồn tại hay không, chúng ta có thể phóng to mảng (một thứ rất chậm) hoặc đơn giản là giảm giá trị tối đa của trình tạo số ngẫu nhiên ( int.MaxValuekhông phải là số "đặc biệt" ... Đây đơn giản là một số rất lớn). Cuối cùng, nếu thuật toán không bị sai lệch bởi tính ổn định của OrderBy, thì bất kỳ phạm vi giá trị nào cũng sẽ cho kết quả tương tự.

Chương trình sau đó kiểm tra một số giá trị, trong phạm vi 1 ... 4096. Nhìn vào kết quả, khá rõ ràng rằng với các giá trị thấp (<128), thuật toán rất sai lệch (4-8%). Với 3 giá trị bạn cần ít nhất r.Next(1024). Nếu bạn làm cho mảng lớn hơn (4 hoặc 5), thì thậm chí r.Next(1024)là không đủ. Tôi không phải là chuyên gia về xáo trộn và toán học, nhưng tôi nghĩ rằng với mỗi bit dài thêm của mảng, bạn cần thêm 2 bit có giá trị tối đa (vì nghịch lý sinh nhật được kết nối với sqrt (chữ số)), vì vậy rằng nếu giá trị tối đa là 2 ^ 31, tôi sẽ nói rằng bạn sẽ có thể sắp xếp các mảng tối đa 2 ^ 12/2 ^ 13 bit (4096-8192 phần tử)


Được nêu rõ, và hiển thị hoàn hảo một vấn đề với câu hỏi ban đầu. Điều này nên được hợp nhất với câu trả lời của Jon.
TheSoftwareJedi

6

Nó có thể ổn cho hầu hết các mục đích và hầu như luôn luôn nó tạo ra một phân phối thực sự ngẫu nhiên (trừ khi Random.Next () tạo ra hai số nguyên ngẫu nhiên giống hệt nhau).

Nó hoạt động bằng cách gán cho mỗi phần tử của chuỗi một số nguyên ngẫu nhiên, sau đó sắp xếp chuỗi theo các số nguyên này.

Nó hoàn toàn chấp nhận được đối với 99,9% ứng dụng (trừ khi bạn thực sự cần xử lý trường hợp cạnh trên). Ngoài ra, sự phản đối của người dùng đối với thời gian chạy của nó là hợp lệ, vì vậy nếu bạn xáo trộn một danh sách dài, bạn có thể không muốn sử dụng nó.


4

Điều này đã đưa ra nhiều lần trước đây. Tìm kiếm Fisher-Yates trên StackOverflow.

Đây là một mẫu mã C # tôi đã viết cho thuật toán này. Bạn có thể tham số hóa nó trên một số loại khác, nếu bạn thích.

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

2
Bạn không nên sử dụng Randomnhư một biến tĩnh như thế này - Randomkhông an toàn cho chuỗi. Xem csharpindepth.com/Articles/Ch CHƯƠNG12 / Random.aspx
Jon Skeet

@Jon Skeet: chắc chắn, đó là một lý lẽ hợp pháp. OTOH, OP đã hỏi về một thuật toán hoàn toàn sai trong khi điều này là chính xác (khác với trường hợp sử dụng xáo trộn thẻ đa luồng).
hughdbrown

1
Điều đó chỉ có nghĩa là điều này "ít sai" hơn so với phương pháp của OP. Điều đó không có nghĩa là mã nên được sử dụng mà không hiểu rằng nó không thể được sử dụng một cách an toàn trong ngữ cảnh đa luồng ... đó là điều bạn không đề cập đến. Có một kỳ vọng hợp lý rằng các thành viên tĩnh có thể được sử dụng một cách an toàn từ nhiều luồng.
Jon Skeet

@Jon Skeet: Chắc chắn, tôi có thể thay đổi nó. Làm xong. Tôi có xu hướng nghĩ rằng quay trở lại một câu hỏi đã trả lời ba năm rưỡi trước và nói: "Không đúng vì nó không xử lý trường hợp sử dụng đa luồng" khi OP không bao giờ hỏi về bất cứ điều gì nhiều hơn thuật toán là quá mức. Xem lại câu trả lời của tôi trong những năm qua. Thường thì tôi đã trả lời OP vượt quá các yêu cầu đã nêu. Tôi đã bị chỉ trích vì điều đó. Mặc dù vậy, tôi không mong đợi OP sẽ nhận được câu trả lời phù hợp với tất cả các mục đích sử dụng có thể.
hughdbrown

Tôi chỉ truy cập câu trả lời này bởi vì một người khác đã chỉ cho tôi về nó trên trò chuyện. Mặc dù OP không đề cập cụ thể đến luồng, tôi nghĩ rằng nó chắc chắn đáng được đề cập khi một phương thức tĩnh không an toàn cho luồng, vì nó không bình thường và làm cho mã không phù hợp trong nhiều tình huống mà không sửa đổi. Mã mới của bạn an toàn cho chuỗi - nhưng vẫn không lý tưởng như khi bạn gọi nó từ nhiều luồng tại "khoảng" cùng một lúc để xáo trộn hai bộ sưu tập có cùng kích thước, các xáo trộn sẽ tương đương. Về cơ bản, Randomlà một nỗi đau để sử dụng, như đã lưu ý trong bài viết của tôi.
Jon Skeet

3

Có vẻ như là một thuật toán xáo trộn tốt, nếu bạn không quá lo lắng về hiệu suất. Vấn đề duy nhất tôi chỉ ra là hành vi của nó không thể kiểm soát được, vì vậy bạn có thể gặp khó khăn khi kiểm tra nó.

Một tùy chọn có thể là có một hạt giống được truyền dưới dạng tham số cho trình tạo số ngẫu nhiên (hoặc trình tạo ngẫu nhiên làm tham số), vì vậy bạn có thể có nhiều quyền kiểm soát hơn và kiểm tra nó dễ dàng hơn.


3

Tôi thấy câu trả lời của Jon Skeet là hoàn toàn thỏa đáng, nhưng máy quét robo của khách hàng của tôi sẽ báo cáo bất kỳ trường hợp nào Randomlà lỗ hổng bảo mật. Vì vậy, tôi trao đổi nó ra cho System.Security.Cryptography.RNGCryptoServiceProvider. Như một phần thưởng, nó khắc phục vấn đề an toàn luồng đã được đề cập. Mặt khác, RNGCryptoServiceProviderđã được đo là chậm hơn 300 lần so với sử dụng Random.

Sử dụng:

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

Phương pháp:

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

3

Tìm kiếm một thuật toán? Bạn có thể sử dụng ShuffleListlớp học của tôi :

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

Sau đó, sử dụng nó như thế này:

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

Làm thế nào nó hoạt động?

Hãy lấy danh sách được sắp xếp ban đầu của 5 số nguyên đầu tiên : { 0, 1, 2, 3, 4 }.

Phương thức này bắt đầu bằng cách đếm các phần tử nubmer và gọi nó count. Sau đó, với countgiảm trên mỗi bước, phải mất một số ngẫu nhiên giữa 0countvà di chuyển nó đến cuối danh sách.

Trong ví dụ từng bước sau đây, các mục có thể được di chuyển là chữ nghiêng , mục được chọn được in đậm :

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


Đó không phải là O (n). Hủy bỏ một mình là O (n).
paparazzo

Hmm, có vẻ như bạn đúng, xấu của tôi! Tôi sẽ xóa phần đó.
SteeveDroz

1

Thuật toán này xáo trộn bằng cách tạo ra một giá trị ngẫu nhiên mới cho mỗi giá trị trong danh sách, sau đó sắp xếp danh sách theo các giá trị ngẫu nhiên đó. Hãy nghĩ về nó như thêm một cột mới vào bảng trong bộ nhớ, sau đó điền nó bằng GUID, sau đó sắp xếp theo cột đó. Trông giống như một cách hiệu quả với tôi (đặc biệt là với đường lambda!)


1

Hơi không liên quan, nhưng đây là một phương pháp thú vị (mặc dù nó thực sự quá mức, đã thực sự được thực hiện) cho thế hệ cuộn xúc xắc thực sự ngẫu nhiên!

Xúc xắc-O-Matic

Lý do tôi đăng bài này ở đây, là vì anh ta đưa ra một số điểm thú vị về cách người dùng của anh ta phản ứng với ý tưởng sử dụng thuật toán để xáo trộn, trên xúc xắc thực tế. Tất nhiên, trong thế giới thực, một giải pháp như vậy chỉ dành cho các đầu cực kỳ thực sự của quang phổ nơi sự ngẫu nhiên có tác động lớn như vậy và có lẽ tác động ảnh hưởng đến tiền bạc;).


1

Tôi muốn nói rằng nhiều câu trả lời ở đây như "Thuật toán này xáo trộn bằng cách tạo ra một giá trị ngẫu nhiên mới cho mỗi giá trị trong danh sách, sau đó sắp xếp danh sách theo các giá trị ngẫu nhiên đó" có thể rất sai!

Tôi nghĩ rằng điều này KHÔNG gán giá trị ngẫu nhiên cho mỗi phần tử của bộ sưu tập nguồn. Thay vào đó, có thể có một thuật toán sắp xếp chạy như Quicksort sẽ gọi hàm so sánh xấp xỉ n log n lần. Một số algortihm thực sự mong đợi chức năng so sánh này sẽ ổn định và luôn trả về kết quả tương tự!

Không thể là IEnumerableSorter gọi hàm so sánh cho từng bước thuật toán, ví dụ quicksort và mỗi lần gọi hàm x => r.Next() cho cả hai tham số mà không lưu các bộ đệm này!

Trong trường hợp đó, bạn có thể thực sự làm xáo trộn thuật toán sắp xếp và làm cho nó tồi tệ hơn nhiều so với kỳ vọng mà thuật toán được xây dựng. Tất nhiên, cuối cùng nó sẽ trở nên ổn định và trả lại một cái gì đó.

Tôi có thể kiểm tra nó sau bằng cách đặt đầu ra gỡ lỗi bên trong chức năng "Tiếp theo" mới để xem điều gì sẽ xảy ra. Trong Reflector tôi không thể ngay lập tức tìm ra cách nó hoạt động.


1
Đây không phải là trường hợp: ghi đè nội bộ void ComputeKeys (TEuity [], int int); Loại khai báo: System.Linq.EnumerableSorter <TEuity, TKey> Hội: System.Core, Version = 3.5.0.0 Hàm này tạo ra một mảng đầu tiên với tất cả các khóa tiêu thụ bộ nhớ, trước khi quicksort sắp xếp chúng
Christian

Điều đó là tốt để biết - mặc dù chỉ là một chi tiết thực hiện, có thể thay đổi có thể hình dung trong các phiên bản trong tương lai!
Blorgbeard ra mắt vào

-5

Thời gian khởi động để chạy trên mã với xóa tất cả các luồng và bộ đệm trong mỗi bài kiểm tra mới,

Mã đầu tiên không thành công. Nó chạy trên LINQPad. Nếu bạn làm theo để kiểm tra mã này.

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 ()) sử dụng 38,6528 ms

list.OrderBy (x => Guid.NewGuid ()) sử dụng 36,7634 ms (Được đề xuất từ ​​MSDN.)

lần thứ hai cả hai sử dụng cùng một lúc.

EDIT: MÃ KIỂM TRA trên Intel Core i7 4@2.1GHz, Ram 8 GB DDR3 @ 1600, HDD SATA 5200 vòng / phút với [Dữ liệu: 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);
            }
            }

        }
    }
}

Mô tả kết quả: https://www.dropbox.com/s/9dw9wl259dfs04g/ResultDescrip.PNG
Kết quả thống kê: https://www.dropbox.com/s/ewq5ybtsvesme4d/ResultStat.PNG

Kết luận:
Giả sử: LINQ OrderBy (r.Next ()) và OrderBy (Guid.NewGuid ()) không tệ hơn Phương thức xáo trộn do người dùng xác định trong Giải pháp đầu tiên.

Trả lời: Chúng mâu thuẫn.


1
Tùy chọn thứ hai không chính xác , và do đó hiệu suất của nó là không liên quan . Điều này cũng vẫn không trả lời được câu hỏi liệu việc đặt hàng theo một số ngẫu nhiên có được chấp nhận, hiệu quả hay không. Giải pháp đầu tiên cũng có vấn đề về tính chính xác, nhưng chúng không phải vấn đề lớn.
Phục vụ

Xin lỗi, tôi muốn biết loại thông số nào tốt hơn của Quicksort của Linq OrderBy? Tôi cần kiểm tra hiệu suất. Tuy nhiên, tôi nghĩ kiểu int chỉ có tốc độ tốt hơn chuỗi Guid nhưng không phải vậy. Tôi hiểu tại sao MSDN đề nghị. Giải pháp hiệu suất đầu tiên được chỉnh sửa giống như OrderBy với thể hiện Ngẫu nhiên.
GMzo

Điểm đo hiệu năng của mã không giải quyết được vấn đề là gì? Hiệu suất chỉ là một xem xét để thực hiện giữa hai giải pháp mà cả hai hoạt động . Khi bạn có giải pháp làm việc, sau đó bạn có thể bắt đầu so sánh chúng.
Phục vụ

Tôi phải có thời gian để kiểm tra nhiều dữ liệu hơn sau đó nếu nó kết thúc, tôi hứa sẽ đăng lại. Giả sử: Tôi nghĩ Linq OrderBy không tệ hơn giải pháp đầu tiên. Ý kiến: Thật dễ sử dụng và dễ hiểu.
GMzo

Nó đáng chú ý là kém hiệu quả hơn các thuật toán xáo trộn đơn giản rất đơn giản, nhưng một lần nữa, hiệu suất là không liên quan . Họ không đáng tin cậy xáo trộn dữ liệu, ngoài việc ít hiệu suất hơn.
Phục vụ
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.