Chọn ngẫu nhiên một danh sách <T>


855

Cách tốt nhất để ngẫu nhiên hóa thứ tự của một danh sách chung trong C # là gì? Tôi đã có một bộ 75 số hữu hạn trong một danh sách mà tôi muốn chỉ định một thứ tự ngẫu nhiên, để vẽ chúng cho một ứng dụng loại xổ số.


3
Có một vấn đề mở để tích hợp chức năng này với .NET: github.com/dotnet/corefx/issues/461
Natan

5
Bạn có thể quan tâm đến gói NuGet này , chứa các phương thức mở rộng để xáo trộn IList <T> và IEnumerable <T> bằng thuật toán Fisher-Yates được đề cập bên dưới
ChaseMedallion

3
@Natan họ đã đóng cửa vấn đề vì ai đó "đã làm việc trên nhiều dự án và phát triển nhiều thư viện và không bao giờ có nhu cầu trong một phương pháp như vậy" khiến tôi bực mình. Bây giờ chúng tôi phải điều tra bản thân, tìm kiếm các triển khai tốt nhất, lãng phí thời gian để chỉ đơn giản là phát minh lại bánh xe.
Vitalii Isaenko

1
Tôi có thấy điều này đúng không? Không phải là một câu trả lời chức năng hợp lệ sau 10 năm? Có lẽ chúng ta cần một tiền thưởng khác cho một giải pháp giải quyết số lượng entropy cần thiết, để xáo trộn một danh sách với 75 số $ log2 (75!) = 364 $ và làm thế nào chúng ta có thể có được điều này. Người ta sẽ cần phải đổi lại ngay cả một RNG an toàn bằng mật mã với 256 bit entropy ít nhất một lần trong một lần xáo trộn của ngư dân.
Falco

1
Và nếu lập trình viên thông thường không thể giải quyết vấn đề này, có phải tất cả chúng ta đã chơi cùng 0,01% các trò chơi solitaire có thể mãi mãi?
Falco

Câu trả lời:


1137

Xáo trộn bất kỳ (I)Listvới một phương thức mở rộng dựa trên xáo trộn 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;  
    }  
}

Sử dụng:

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

Đoạn mã trên sử dụng phương thức System.Random bị chỉ trích nhiều để chọn ứng viên hoán đổi. Nó nhanh nhưng không ngẫu nhiên như nó phải vậy. Nếu bạn cần chất lượng ngẫu nhiên tốt hơn trong các xáo trộn của mình, hãy sử dụng trình tạo số ngẫu nhiên trong System.Security.Cryptography như vậy:

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

Một so sánh đơn giản có sẵn tại blog này (WayBack Machine).

Chỉnh sửa: Kể từ khi viết câu trả lời này vài năm trước, nhiều người đã bình luận hoặc viết thư cho tôi, để chỉ ra lỗ hổng ngớ ngẩn lớn trong so sánh của tôi. Họ tất nhiên là đúng. Không có gì sai với System.Random nếu nó được sử dụng theo cách nó được dự định. Trong ví dụ đầu tiên của tôi ở trên, tôi khởi tạo biến rng bên trong phương thức Shuffle, yêu cầu sự cố nếu phương thức này sẽ được gọi nhiều lần. Dưới đây là một ví dụ đầy đủ, cố định dựa trên một nhận xét thực sự hữu ích nhận được ngày hôm nay từ @weston ở đây trên SO.

Chương trình.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;
      }
    }
  }
}

32
Điều gì xảy ra nếu list.Count là> Byte.MaxValue? Nếu n = 1000, thì 255/1000 = 0, do đó vòng lặp do sẽ là một vòng lặp vô hạn vì hộp [0] <0 luôn luôn sai.
AndrewS

18
Tôi muốn chỉ ra rằng, sự so sánh là thiếu sót. Sử dụng <code> new Random () </ code> trong một vòng lặp là vấn đề, không phải là tính ngẫu nhiên của <code> Random </ code> Giải thích
Sven

9
Đó là một ý tưởng tốt để truyền một thể hiện của Random cho phương thức Shuffle thay vì tạo nó bên trong như thể bạn đang gọi Shuffle nhiều lần liên tiếp (ví dụ: xáo trộn rất nhiều danh sách ngắn), tất cả các danh sách sẽ được xáo trộn trong cùng một danh sách cách (ví dụ: mục đầu tiên luôn được chuyển đến vị trí 3).
Mark Heath

7
Chỉ cần làm Random rng = new Random();một staticsẽ giải quyết vấn đề trong bài so sánh. Vì mỗi cuộc gọi tiếp theo sẽ tiếp tục từ các cuộc gọi trước kết quả ngẫu nhiên cuối cùng.
weston

5
# 2, không rõ phiên bản với trình tạo Crypto hoạt động vì phạm vi tối đa của một byte là 255, do đó, bất kỳ danh sách nào lớn hơn sẽ không xáo trộn chính xác.
Mark Sowul

336

Nếu chúng ta chỉ cần xáo trộn các mục theo thứ tự hoàn toàn ngẫu nhiên (chỉ để trộn các mục trong danh sách), tôi thích mã đơn giản nhưng hiệu quả này để sắp xếp các mục theo hướng dẫn ...

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

40
GUID có nghĩa là duy nhất không ngẫu nhiên. Một phần của nó là dựa trên máy và một phần khác dựa trên thời gian và chỉ một phần nhỏ là ngẫu nhiên. blog.msdn.com/b/oldnewthing/archive/2008/06/27/8659071.aspx
Despertar

99
Đây là một giải pháp thanh lịch tốt đẹp. Nếu bạn muốn một cái gì đó ngoài một hướng dẫn để tạo ra sự ngẫu nhiên, chỉ cần đặt hàng bởi một thứ khác. Ví dụ: var shuffledcards = cards.OrderBy(a => rng.Next()); compilr.com/grenade/sandbox/Program.cs
lựu đạn

20
Xin đừng. Cái này sai. "Đặt hàng ngẫu nhiên" hoàn toàn KHÔNG phải là một sự xáo trộn: bạn giới thiệu một sự thiên vị và tệ hơn nữa, bạn có nguy cơ đi vào các vòng lặp vô hạn
Vito De Tullio

78
@VitoDeTullio: Bạn đang sai lầm. Bạn có nguy cơ vòng lặp vô hạn khi bạn cung cấp một hàm so sánh ngẫu nhiên ; một chức năng so sánh là cần thiết để tạo ra một tổng thứ tự nhất quán . Một khóa ngẫu nhiên là tốt. Gợi ý này là sai vì các hướng dẫn không được đảm bảo là ngẫu nhiên , không phải vì kỹ thuật sắp xếp theo khóa ngẫu nhiên là sai.
Eric Lippert

24
@Doug: NewGuidchỉ đảm bảo rằng nó mang lại cho bạn một GUID duy nhất. Nó không đảm bảo về tính ngẫu nhiên. Nếu bạn đang sử dụng GUID cho mục đích khác ngoài việc tạo một giá trị duy nhất , bạn đã làm sai.
Eric Lippert

120

Tôi hơi ngạc nhiên bởi tất cả các phiên bản mạnh mẽ của thuật toán đơn giản này ở đây. Fisher-Yates (hay Knuth shuffle) hơi phức tạp nhưng rất nhỏ gọn. Tại sao nó khó khăn? Bởi vì bạn cần chú ý xem liệu trình tạo số ngẫu nhiên của bạn r(a,b)trả về giá trị ở đâu blà bao gồm hay độc quyền. Tôi cũng đã chỉnh sửa mô tả Wikipedia để mọi người không mù quáng theo mã giả ở đó và khó phát hiện lỗi. Đối với .Net, Random.Next(a,b)trả về số độc quyền bnhư vậy mà không cần phải quảng cáo thêm, đây là cách có thể triển khai trong 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;
}

Hãy thử mã này .


Sẽ không tốt hơn nếu thay đổi rnd (i, list.Count) thành rnd (0, list.Count) để bất kỳ thẻ nào có thể được hoán đổi?
Bánh rán

16
@Donuts - không. Nếu bạn làm điều đó, bạn sẽ thêm sự thiên vị trong shuffle.
Shital Shah

2
Bằng cách tách Swap <T> ra một phương thức riêng biệt, có vẻ như bạn gây ra rất nhiều phân bổ T không cần thiết cho temp.
Clay

2
Tôi cho rằng LINQ có khả năng làm chậm hiệu suất của việc xáo trộn và đó là lý do không sử dụng nó, đặc biệt là với sự đơn giản tương đối của mã.
winglerw28

7
Khi i = list.Count - 1, tức là lần lặp cuối cùng, rnd.Next(i, list.Count)sẽ trả lại cho bạn. Do đó, bạn cần i < list.Count -1như điều kiện vòng lặp. Chà, bạn không 'cần' nó, nhưng nó tiết kiệm được 1 lần lặp;)
Pod

78

Phương pháp mở rộng cho IEnumerable:

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

3
Lưu ý rằng điều này không an toàn cho chuỗi, ngay cả khi được sử dụng trong danh sách an toàn chủ đề
BlueRaja - Danny Pflughoeft

1
Làm thế nào để chúng tôi cung cấp danh sách <chuỗi> cho chức năng này?
MonsterMMORPG

8
Có hai vấn đề quan trọng với thuật toán này: - OrderBysử dụng biến thể QuickSort để sắp xếp các mục theo các khóa (có vẻ ngẫu nhiên) của chúng. Hiệu suất QuickSort là O (N log N) ; ngược lại, một shuffle Fisher-Yates là O (N) . Đối với một bộ sưu tập gồm 75 yếu tố, điều này có thể không phải là vấn đề lớn, nhưng sự khác biệt sẽ trở nên rõ rệt đối với các bộ sưu tập lớn hơn.
John Beyer

10
... - Random.Next()có thể tạo ra phân phối giá trị giả ngẫu nhiên hợp lý, nhưng không đảm bảo rằng các giá trị sẽ là duy nhất. Xác suất của các khóa trùng lặp tăng lên (phi tuyến tính) với N cho đến khi đạt được sự chắc chắn khi N đạt 2 ^ 32 + 1. Các OrderBySắp xếp nhanh là ổn định loại; do đó, nếu nhiều phần tử xảy ra để được gán cùng một giá trị chỉ mục giả ngẫu nhiên, thì thứ tự của chúng trong chuỗi đầu ra sẽ giống như trong chuỗi đầu vào; do đó, một thiên vị được đưa vào "xáo trộn".
John Beyer

27
@JohnBeyer: Có nhiều vấn đề lớn hơn nhiều so với nguồn sai lệch đó. Chỉ có bốn tỷ hạt giống có thể là Ngẫu nhiên, rất xa, ít hơn nhiều so với số lượng xáo trộn có thể có của một bộ có kích thước vừa phải. Chỉ một phần rất nhỏ của các xáo trộn có thể được tạo ra. Sự thiên vị đó lấn át sự thiên vị do va chạm vô tình.
Eric Lippert

16

Ý tưởng là lấy đối tượng ẩn danh với mục và thứ tự ngẫu nhiên, sau đó sắp xếp lại các mục theo thứ tự này và trả về giá trị:

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

3
giải pháp lót tốt nhất
vipin8169

1
Bạn đang thiếu một dấu chấm phẩy ở cuối fyi
reggaeg Ức

Nếu bất cứ ai không chắc chắn về rnd, hãy thêm điều này trước đoạn mã trên 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;
    }


4
Bạn không nên làm gì đó như var listCopy = list.ToList()để tránh bật tất cả các mục ra khỏi danh sách đến? Tôi thực sự không thấy lý do tại sao bạn muốn biến đổi những danh sách đó thành trống.
Chris Marisic

9

EDIT Điểm RemoveAtyếu trong phiên bản trước của tôi. Giải pháp này khắc phục điều đó.

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

Lưu ý tùy chọn Random generator, nếu việc triển khai khung cơ sở Randomkhông an toàn theo luồng hoặc mã hóa đủ mạnh cho nhu cầu của bạn, bạn có thể đưa triển khai của mình vào hoạt động.

Randomthể tìm thấy một triển khai phù hợp cho việc triển khai mã hóa an toàn theo luồng an toàn trong câu trả lời này.


Đây là một ý tưởng, mở rộng IList theo cách hiệu quả (hy vọng).

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


Xem stackoverflow.com/questions/4412405/ Lần . bạn phải nhận thức được
nawfal

@nawfal thấy tôi thực hiện cải tiến.
Jodrell

1
hmm đủ công bằng Là nó GetNexthay Next?
nawfal

4

Bạn có thể đạt được điều đó bằng cách sử dụng phương pháp mở rộng đơn giản này

public static class IEnumerableExtensions
{

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

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

và bạn có thể sử dụng nó bằng cách làm như sau

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

3
Tôi sẽ giữ Randomcá thể lớp bên ngoài hàm như một staticbiến. Nếu không, bạn có thể nhận được hạt giống ngẫu nhiên tương tự từ bộ đếm thời gian nếu được gọi liên tiếp.
Lemonseed

Một lưu ý thú vị - nếu bạn khởi tạo nhanh chóng lớp Ngẫu nhiên trong một vòng lặp, giả sử trong khoảng từ 0 ms đến 200 ms mỗi lần, thì bạn có cơ hội rất cao để có được hạt giống ngẫu nhiên giống nhau - từ đó dẫn đến kết quả lặp lại. Tuy nhiên, bạn có thể khắc phục điều này bằng cách sử dụng Random rand = new Random (Guid.NewGuid (). GetHashCode ()); Điều này thực sự buộc sự ngẫu nhiên bắt nguồn từ Guid.NewGuid ()
Baaleos

4

Đây là phương pháp xáo trộn ưa thích của tôi khi muốn không sửa đổi bản gốc. Đây là một biến thể của thuật toán "từ trong ra ngoài" của FisherTHER Yates hoạt động trên bất kỳ chuỗi nào có thể đếm được (độ dài sourcekhông cần phải biết từ đầu).

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

Thuật toán này cũng có thể được thực hiện bằng cách phân bổ một phạm vi từ 0đếnlength - 1 và làm cạn kiệt ngẫu nhiên các chỉ số bằng cách hoán đổi chỉ mục được chọn ngẫu nhiên với chỉ mục cuối cùng cho đến khi tất cả các chỉ số được chọn chính xác một lần. Mã ở trên hoàn thành điều tương tự chính xác nhưng không có phân bổ bổ sung. Mà là khá gọn gàng.

Liên quan đến Randomlớp học, đây là trình tạo số mục đích chung (và nếu tôi đang chạy xổ số, tôi sẽ cân nhắc sử dụng một cái gì đó khác biệt). Nó cũng dựa trên giá trị hạt giống dựa trên thời gian theo mặc định. Một vấn đề nhỏ của vấn đề là tạo hạt giống cho Randomlớp RNGCryptoServiceProviderhoặc bạn có thể sử dụng RNGCryptoServiceProviderphương thức tương tự như thế này (xem bên dưới) để tạo các giá trị dấu phẩy động ngẫu nhiên được chọn thống nhất nhưng chạy xổ số khá nhiều đòi hỏi phải hiểu ngẫu nhiên và bản chất của nguồn ngẫu nhiên.

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

Điểm tạo ra một gấp đôi ngẫu nhiên (chỉ từ 0 đến 1) là sử dụng để chia tỷ lệ thành một giải pháp số nguyên. Nếu bạn cần chọn một cái gì đó từ một danh sách dựa trên một đôi ngẫu nhiên x, điều luôn luôn sẽ 0 <= x && x < 1là thẳng về phía trước.

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

Thưởng thức!


4

Nếu bạn không phiền khi sử dụng hai Lists, thì đây có lẽ là cách dễ nhất để làm điều đó, nhưng có lẽ không phải là cách hiệu quả nhất hoặc không thể đoán trước:

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

3

Nếu bạn có một số cố định (75), bạn có thể tạo một mảng với 75 phần tử, sau đó liệt kê danh sách của bạn, di chuyển các phần tử đến các vị trí ngẫu nhiên trong mảng. Bạn có thể tạo ánh xạ số danh sách thành chỉ mục mảng bằng cách sử dụng xáo trộn Fisher-Yates .


3

Tôi thường sử dụng:

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

list.RemoveAt là một hoạt động O (n), khiến cho việc thực hiện này bị chậm một cách nghiêm trọng.
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.

0

Đây là một Shuffler hiệu quả trả về một mảng byte của các giá trị được xáo trộn. Nó không bao giờ xáo trộn nhiều hơn mức cần thiết. Nó có thể được khởi động lại từ nơi mà trước đó nó đã rời đi. Triển khai thực tế của tôi (không hiển thị) là một thành phần MEF cho phép người dùng thay thế chỉ định thay đổi.

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

`


0

Đây là một cách an toàn chủ đề để làm điều này:

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

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

}

2
Chào mừng bạn đến với Stack Overflow! Vui lòng xem xét thêm một số giải thích cho câu trả lời của bạn, thay vì chỉ là một khối mã lớn. Mục tiêu của chúng tôi ở đây là giáo dục mọi người để họ hiểu câu trả lời và có thể áp dụng nó trong các tình huống khác. Nếu bạn nhận xét mã của mình và thêm một lời giải thích, bạn sẽ làm cho câu trả lời của mình hữu ích hơn không chỉ cho người hỏi câu hỏi lần này mà còn cho bất kỳ ai trong tương lai có thể gặp vấn đề tương tự.
starplusplus

4
Hầu hết các mã này hoàn toàn không liên quan đến câu hỏi và phần hữu ích duy nhất về cơ bản lặp lại câu trả lời của Adam Tegen từ gần 6 năm trước.
TC

0

Một sửa đổi đơn giản của câu trả lời được chấp nhận trả về một danh sách mới thay vì làm việc tại chỗ và chấp nhận tổng quát hơn IEnumerable<T>như nhiều phương pháp Linq khác làm.

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


-5

Chắc chắn bài cũ, nhưng tôi chỉ sử dụng GUID.

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

GUID luôn là duy nhất và vì nó được tạo lại mỗi lần kết quả thay đổi mỗi lần.


Nhỏ gọn, nhưng bạn có tham khảo về việc sắp xếp các newGuids liên tiếp để có chất lượng cao ngẫu nhiên không? Một số phiên bản của quid / uuid có tem thời gian và các phần không ngẫu nhiên khác.
Johan Lundberg

8
Câu trả lời này đã được đưa ra, và tệ hơn là nó được thiết kế cho tính duy nhất không phải là ngẫu nhiên.
Alex Angas

-7

Một cách tiếp cận rất đơn giản cho loại vấn đề này là sử dụng một số trao đổi phần tử ngẫu nhiên trong danh sách.

Trong mã giả, nó sẽ trông như thế này:

do 
    r1 = randomPositionInList()
    r2 = randomPositionInList()
    swap elements at index r1 and index r2 
for a certain number of times

1
Một vấn đề với phương pháp này là biết khi nào nên dừng lại. Nó cũng có xu hướng phóng đại bất kỳ sự thiên vị nào trong trình tạo số giả ngẫu nhiên.
Mark Bessey

3
Đúng. Không hiệu quả cao. Không có lý do để sử dụng một cách tiếp cận như thế này khi các cách tiếp cận tốt hơn, nhanh hơn tồn tại mà chỉ đơn giản như vậy.
Peter ALLenWebb

1
không hiệu quả hoặc hiệu quả ... Chạy nó N lần có thể sẽ để lại nhiều yếu tố ở vị trí ban đầu của chúng.
NSjonas
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.