2009-10-30 34 views

Trả lời

33

Một lựa chọn khác là sử dụng OrderBy và để sắp xếp trên một giá trị GUID, mà bạn có thể làm như vậy bằng:

var result = sequence.OrderBy(elem => Guid.NewGuid()); 

tôi đã làm một số xét nghiệm thực nghiệm để thuyết phục bản thân mình rằng trên thực sự tạo ra một phân phối ngẫu nhiên (mà nó xuất hiện làm). Bạn có thể xem kết quả của tôi tại Techniques for Randomly Reordering an Array.

+16

Giải pháp này vi phạm hợp đồng đặt hàng, cụ thể là một đối tượng nhất định có khóa nhất quán trong toàn bộ sắp xếp. Nếu nó hoạt động, nó làm như vậy thông qua cơ hội một mình, và có thể phá vỡ trong các phiên bản tương lai của khuôn khổ. Để biết thêm chi tiết, hãy xem http://blogs.msdn.com/b/ericlippert/archive/2011/01/31/spot-the-defect-bad-comparisons-part-four.aspx –

+0

Bắt tốt. Tuy nhiên, nếu điều này được cam kết với một bộ sưu tập thực tế - chẳng hạn như thêm .ToList() hoặc .ToArray() vào cuối -, thì không có cơ hội nào của bộ sưu tập nhận mã LINQ được xử lý/lặp lại nhiều lần để thực hiện sắp xếp. Tôi tưởng tượng rằng sẽ tồn tại trong tương lai nâng cấp cũng vì nó sẽ hoạt động như một ảnh chụp dự đoán được. – MutantNinjaCodeMonkey

+5

Tôi chỉ tình cờ gặp trang này và nghĩ nó thật tuyệt vời, nhưng tôi không hiểu lời bình luận. những gì có thể có thể đi sai trong việc sử dụng phương pháp này để trộn một bộ sưu tập (tôi không hỏi rằng mỉa mai, tôi thực sự tò mò về khả năng)? – SelAromDotNet

6

Shuffle việc thu thập theo thứ tự ngẫu nhiên và lấy n mục đầu tiên từ kết quả.

+6

Lưu ý rằng bạn không phải trộn hoàn toàn bộ sưu tập khi n

98

Tiếp tục câu trả lời mquander và bình luận Dan Blanchard của, đây là một phương pháp khuyến nông LINQ thân thiện mà thực hiện một Fisher-Yates-Durstenfeld shuffle:

// take n random items from yourCollection 
var randomItems = yourCollection.Shuffle().Take(n); 

// ... 

public static class EnumerableExtensions 
{ 
    public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source) 
    { 
     return source.Shuffle(new Random()); 
    } 

    public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source, Random rng) 
    { 
     if (source == null) throw new ArgumentNullException("source"); 
     if (rng == null) throw new ArgumentNullException("rng"); 

     return source.ShuffleIterator(rng); 
    } 

    private static IEnumerable<T> ShuffleIterator<T>(
     this IEnumerable<T> source, Random rng) 
    { 
     var buffer = source.ToList(); 
     for (int i = 0; i < buffer.Count; i++) 
     { 
      int j = rng.Next(i, buffer.Count); 
      yield return buffer[j]; 

      buffer[j] = buffer[i]; 
     } 
    } 
} 
+3

+1 Linqy và hiệu quả. Mũ tắt :) –

+0

Có phải tôi hoặc phương pháp này sẽ làm hỏng chỉ mục danh sách? Bất cứ khi nào sử dụng elementAt() sau khi xáo trộn kết quả tôi nhận được là hoàn toàn bất ngờ ... – Htbaa

+2

@Htbaa: Phương thức trả về một chuỗi được đánh giá lười biếng. Nếu bạn thực hiện 'seq.Shuffle(). ElementAt (n)' nhiều lần thì bạn đang xáo trộn mỗi lần, vì vậy có khả năng bạn sẽ nhận được một mục khác ở vị trí 'n'. Nếu bạn muốn shuffle * once * thì bạn cần lưu trữ chuỗi trong một tập hợp cụ thể của một số loại: ví dụ, 'var list = seq.Shuffle(). ToList()'. Sau đó, bạn có thể làm 'list.ElementAt (n)' thường xuyên như bạn muốn - hoặc chỉ 'list [n]' - và bạn sẽ luôn nhận được cùng một mục. – LukeH

-3

Xin lỗi vì xấu xí đang :-), nhưng

 

var result =yourCollection.OrderBy(p => (p.GetHashCode().ToString() + Guid.NewGuid().ToString()).GetHashCode()).Take(n); 
 
+0

'p.GetHashCode(). ToString() + Guid.NewGuid(). ToString()). GetHashCode()' không phải là ngẫu nhiên. Nó có thể trông ngẫu nhiên, nhưng nó không phải. Bạn nên sử dụng RNG cho ngẫu nhiên - các nhà khoa học dữ liệu đã thiết kế chúng một cách cụ thể để có thể ngẫu nhiên nhất có thể. – Enigmativity

10

này có một số vấn đề với "xu hướng ngẫu nhiên" và tôi chắc chắn nó không tối ưu, đây là một khả năng khác:

var r = new Random(); 
l.OrderBy(x => r.NextDouble()).Take(n); 
+0

Điều này không có bất kỳ sai lệch ngẫu nhiên nào ngoài bất kỳ sai lệch không đáng kể nào từ 'Random'. Nó cũng rất hiệu quả. – Enigmativity

+0

Đến đây để đề xuất điều này. Đây sẽ là câu trả lời được chấp nhận. –

-1

Tôi viết những dòng này sẽ ghi đè phương pháp:

public static IEnumerable<T> Randomize<T>(this IEnumerable<T> items) where T : class 
{ 
    int max = items.Count(); 
    var secuencia = Enumerable.Range(1, max).OrderBy(n => n * n * (new Random()).Next()); 

    return ListOrder<T>(items, secuencia.ToArray()); 
} 

private static IEnumerable<T> ListOrder<T>(IEnumerable<T> items, int[] secuencia) where T : class 
     { 
      List<T> newList = new List<T>(); 
      int count = 0; 
      foreach (var seed in count > 0 ? secuencia.Skip(1) : secuencia.Skip(0)) 
      { 
       newList.Add(items.ElementAt(seed - 1)); 
       count++; 
      } 
      return newList.AsEnumerable<T>(); 
     } 

Sau đó, tôi có danh sách nguồn của tôi (tất cả các mục)

var listSource = p.Session.QueryOver<Listado>(() => pl) 
         .Where(...); 

Cuối cùng, tôi gọi là "Randomize" và tôi nhận được một ngẫu nhiên phụ thu các mặt hàng, trong trường hợp của tôi, 5 hạng mục:

var SubCollection = Randomize(listSource.List()).Take(5).ToList(); 
+0

Bạn đang lãng phí bộ nhớ bằng cách tạo 'newList'. Có thể xem xét sử dụng 'lợi nhuận' bên trong câu lệnh' foreach'. – bradlis7

+0

Dòng 'Enumerable.Range (1, max) .OrderBy (n => n * n * (new Random()). Next())' là khủng khiếp - nó không phải ngẫu nhiên chút nào. Tiếp theo() 'như vậy sẽ chỉ trả về cùng một số trong hầu hết các trường hợp và' n * n' làm cho số bị sai lệch - và có thể có thể gây ra các ngoại lệ tràn. – Enigmativity

0

Một chút ít ngẫu nhiên, nhưng hiệu quả:

var rnd = new Random(); 
var toSkip = list.Count()-n; 

if (toSkip > 0) 
    toSkip = rnd.Next(toSkip); 
else 
    toSkip=0; 

var randomlySelectedSequence = list.Skip(toSkip).Take(n); 
Các vấn đề liên quan