2010-09-07 13 views
6

Tôi đã thực hiện một lớp (mã dưới đây) để xử lý việc tạo ra một "phù hợp với" mục đố trên một bài kiểm tra, đây là kết quả:Làm thế nào tôi có thể có được sự ngẫu nhiên thực sự trong lớp học này mà không có Thread.Sleep (300)?

alt text

Nó hoạt động tốt.

Tuy nhiên, để có được nó hoàn toàn ngẫu nhiên, tôi có phải đặt thread ngủ cho ít nhất 300 đếm giữa xáo trộn ngẫu nhiên của hai cột, bất cứ điều gì thấp hơn 300 lợi nhuận cả các cột được sắp xếp trong cùng trật tự, như thể nó đang sử dụng hạt giống về tính ngẫu nhiên:

LeftDisplayIndexes.Shuffle(); 
Thread.Sleep(300); 
RightDisplayIndexes.Shuffle(); 

gì làm tôi phải làm gì để làm cho xáo trộn của hai cột hoàn toàn ngẫu nhiên mà không cần thời gian này chờ đợi?

mã đầy đủ:

using System.Collections.Generic; 
using System; 
using System.Threading; 

namespace TestSort727272 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      MatchingItems matchingItems = new MatchingItems(); 
      matchingItems.Add("one", "111"); 
      matchingItems.Add("two", "222"); 
      matchingItems.Add("three", "333"); 
      matchingItems.Add("four", "444"); 
      matchingItems.Setup(); 

      matchingItems.DisplayTest(); 
      matchingItems.DisplayAnswers(); 

      Console.ReadLine(); 

     } 
    } 

    public class MatchingItems 
    { 
     public List<MatchingItem> Collection { get; set; } 
     public List<int> LeftDisplayIndexes { get; set; } 
     public List<int> RightDisplayIndexes { get; set; } 

     private char[] _numbers = { '1', '2', '3', '4', '5', '6', '7', '8' }; 
     private char[] _letters = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' }; 

     public MatchingItems() 
     { 
      Collection = new List<MatchingItem>(); 
      LeftDisplayIndexes = new List<int>(); 
      RightDisplayIndexes = new List<int>(); 
     } 

     public void Add(string leftText, string rightText) 
     { 
      MatchingItem matchingItem = new MatchingItem(leftText, rightText); 
      Collection.Add(matchingItem); 
      LeftDisplayIndexes.Add(Collection.Count - 1); 
      RightDisplayIndexes.Add(Collection.Count - 1); 
     } 

     public void DisplayTest() 
     { 
      Console.WriteLine(""); 
      Console.WriteLine("--TEST:-------------------------"); 
      for (int i = 0; i < Collection.Count; i++) 
      { 
       int leftIndex = LeftDisplayIndexes[i]; 
       int rightIndex = RightDisplayIndexes[i]; 
       Console.WriteLine("{0}. {1,-12}{2}. {3}", _numbers[i], Collection[leftIndex].LeftText, _letters[i], Collection[rightIndex].RightText); 
      } 
     } 
     public void DisplayAnswers() 
     { 
      Console.WriteLine(""); 
      Console.WriteLine("--ANSWERS:-------------------------"); 
      for (int i = 0; i < Collection.Count; i++) 
      { 
       string leftLabel = _numbers[i].ToString(); 
       int leftIndex = LeftDisplayIndexes[i]; 
       int rightIndex = RightDisplayIndexes.IndexOf(leftIndex); 
       string answerLabel = _letters[rightIndex].ToString(); 

       Console.WriteLine("{0}. {1}", leftLabel, answerLabel); 

      } 
     } 

     public void Setup() 
     { 
      do 
      { 
       LeftDisplayIndexes.Shuffle(); 
       Thread.Sleep(300); 
       RightDisplayIndexes.Shuffle(); 
      } while (SomeLinesAreMatched()); 
     } 

     private bool SomeLinesAreMatched() 
     { 
      for (int i = 0; i < LeftDisplayIndexes.Count; i++) 
      { 
       int leftIndex = LeftDisplayIndexes[i]; 
       int rightIndex = RightDisplayIndexes[i]; 
       if (leftIndex == rightIndex) 
        return true; 
      } 
      return false; 
     } 


     public void DisplayAsAnswer(int numberedIndex) 
     { 
      Console.WriteLine(""); 
      Console.WriteLine("--ANSWER TO {0}:-------------------------", _numbers[numberedIndex]); 
      for (int i = 0; i < Collection.Count; i++) 
      { 
       int leftIndex = LeftDisplayIndexes[i]; 
       int rightIndex = RightDisplayIndexes[i]; 

       Console.WriteLine("{0}. {1,-12}{2}. {3}", _numbers[i], Collection[leftIndex].LeftText, _letters[i], Collection[rightIndex].RightText); 
      } 
     } 
    } 

    public class MatchingItem 
    { 
     public string LeftText { get; set; } 
     public string RightText { get; set; } 

     public MatchingItem(string leftText, string rightText) 
     { 
      LeftText = leftText; 
      RightText = rightText; 
     } 
    } 

    public static class Helpers 
    { 
     public static void Shuffle<T>(this IList<T> list) 
     { 
      Random rng = new Random(); 
      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; 
      } 
     } 
    } 

} 
+0

Bạn nên google "ngẫu nhiên" bạn sẽ ngạc nhiên về mức độ khó khăn này và khó thực hiện ... (thậm chí, ít thỏa thuận về "ngẫu nhiên" có ý nghĩa gì) –

+2

@Charles : Tôi nghĩ câu trả lời cho vấn đề của anh ấy cơ bản hơn nhiều. ông không có nghĩa là "thật sự ngẫu nhiên" theo nghĩa toán học, chỉ "hai cột này không nên được sắp xếp theo cùng một cách" = P. – Claudiu

+0

có thể trùng lặp của [Ngẫu nhiên phút trong C# 2.0] (http://stackoverflow.com/questions/1167440/random-minutes-in-c-2-0) –

Trả lời

6

Chỉ tạo một phiên bản của lớp Random. Khi bạn gọi nó mà không có một nhà xây dựng nó lấy một hạt giống ngẫu nhiên từ đồng hồ máy tính, vì vậy bạn có thể nhận được cùng một một lần hai lần.

public static class Helpers 
{ 
    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; 
     } 
    } 
} 
+3

Xem ra nếu bạn đang sử dụng nhiều luồng, mặc dù - như với hầu hết các lớp .net Framework, các đối tượng 'Random' không được bảo đảm an toàn chỉ. – cHao

10

Move Random rng = new Random(); cho một biến tĩnh.

MSDN nói "Giá trị mặc định của hạt giống bắt nguồn từ đồng hồ hệ thống và có độ phân giải hữu hạn". Khi bạn tạo nhiều đối tượng Random trong một khoảng thời gian nhỏ, tất cả chúng đều có cùng một hạt giống và giá trị đầu tiên sẽ bằng với tất cả các đối tượng Ngẫu nhiên.

Bằng cách tái sử dụng cùng một đối tượng Random, bạn sẽ chuyển sang giá trị ngẫu nhiên tiếp theo từ một hạt giống đã cho.

2

Vấn đề là bạn đang tạo các đối tượng Random quá gần nhau đúng lúc. Khi bạn làm điều đó, các trình tạo giả ngẫu nhiên bên trong của chúng sẽ được gieo giống với cùng một thời gian hệ thống, và chuỗi các số chúng tạo ra sẽ giống hệt nhau.

Giải pháp đơn giản nhất là sử dụng lại một đối tượng Random đơn lẻ, hoặc bằng cách chuyển nó làm đối số cho thuật toán shuffle của bạn hoặc lưu nó dưới dạng biến thành viên của lớp trong đó shuffle được triển khai.

4

Tôi có phải đặt thread ngủ cho ít nhất 300 đếm giữa ngẫu nhiên xáo trộn của hai cột, bất cứ điều gì thấp hơn 300 lợi nhuận cả cột được sắp xếp theo thứ tự, như thể nó là sử dụng cùng một hạt giống cho ngẫu nhiên

Bạn đã trả lời câu hỏi của riêng bạn ở đây. Nó là "giống như nó đang sử dụng cùng một hạt giống" bởi vì sử dụng cùng một hạt giống! Do độ chi tiết tương đối thô của đồng hồ hệ thống Windows, nhiều trường hợp Random được xây dựng tại cùng thời điểm sẽ có cùng giá trị hạt giống.

Albin suggests, bạn chỉ nên có một đối tượng Random và sử dụng điều đó. Bằng cách này thay vì một chuỗi các chuỗi giả ngẫu nhiên bắt đầu từ cùng một hạt giống và do đó giống hệt nhau, phương pháp Shuffle của bạn sẽ dựa trên một chuỗi giả ngẫu nhiên duy nhất.

Xem xét rằng bạn có nó như là một phương pháp mở rộng, bạn có thể mong muốn nó có thể tái sử dụng được. Trong trường hợp này, hãy xem xét việc có một tình trạng quá tải mà chấp nhận một Random và điều đó không có:

static void Shuffle<T>(this IList<T> list, Random random) 
{ 
    // Your code goes here. 
} 

static void Shuffle<T>(this IList<T> list) 
{ 
    list.Shuffle(new Random()); 
} 

Điều này cho phép người gọi để cung cấp một Random đối tượng tĩnh nếu anh/cô ấy sẽ được gọi Shuffle nhiều lần liên tiếp; mặt khác, nếu nó chỉ là một điều một lần, Shuffle có thể tự chăm sóc bản thân số Random.

Một cuối cùng điều tôi muốn chỉ ra là vì các giải pháp liên quan đến việc sử dụng một đối tượng chia sẻ Random duy nhất, bạn nên biết rằng lớp Random không phải là thread-safe. Nếu có một cơ hội bạn có thể gọi Shuffle từ nhiều chủ đề đồng thời, bạn sẽ cần phải khóa cuộc gọi Next (hoặc: điều tôi thích làm là có đối tượng [ThreadStatic] Random cho mỗi luồng, mỗi chuỗi được tạo thành giá trị ngẫu nhiên do "core" Random - nhưng có liên quan nhiều hơn một chút).

Nếu không, bạn có thể kết thúc bằng Next đột nhiên chỉ cần làm lại chuỗi vô hạn số 0.

+1

Như thường lệ, không phải là câu trả lời đầu tiên, nhưng chắc chắn là hoàn chỉnh nhất;) – serg10

0

Cách thức các trình tạo ngẫu nhiên hoạt động, gần đúng, là chúng có một hạt giống mà từ đó các giá trị ngẫu nhiên bắt nguồn. Khi bạn tạo một đối tượng ngẫu nhiên mới, hạt giống này được đặt là thời gian hệ thống hiện tại, tính bằng giây hoặc mili giây.

Giả sử khi bạn tạo đối tượng Ngẫu nhiên đầu tiên, hạt giống là 10000. Sau khi gọi nó ba lần, các hạt là 20000, 40000, 80000, tạo bất kỳ số nào tạo thành hạt (giả sử 5, 6, 2). Nếu bạn tạo một đối tượng ngẫu nhiên mới rất nhanh, cùng một hạt giống sẽ được sử dụng, 10000. Vì vậy, nếu bạn gọi nó ba lần, bạn sẽ nhận được cùng một hạt giống, 20000, 40000, và 80000, và cùng một số từ chúng.

Tuy nhiên, nếu bạn sử dụng lại cùng một đối tượng, hạt giống mới nhất là 80000, vì vậy thay vào đó bạn sẽ tạo ra ba hạt giống mới, 160000, 320000 và 640000, rất có khả năng cung cấp cho bạn giá trị mới.

Đó là lý do tại sao bạn phải sử dụng một trình tạo ngẫu nhiên mà không phải tạo bộ tạo mới mỗi lần.

0

Cố gắng sử dụng Random() chỉ một lần. Bạn sẽ nhận được ý tưởng.

Các vấn đề liên quan