2013-02-03 40 views
25

thể trùng lặp:
Random number generator only generating one random numberNhiều số ngẫu nhiên đều giống nhau

Một câu hỏi mới bắt đầu. Tôi có một chương trình rất đơn giản vẽ một đường và tôi muốn ngẫu nhiên các vị trí, nhưng mỗi lần tôi tạo một thể hiện ngẫu nhiên mới, nó trả về cùng một giá trị. Vấn đề ở đâu? Cảm ơn bạn.

private void Draw() 
{ 
    Random random1 = new Random(); 
    int randomNumber1 = random1.Next(0, 300); 
    Random random2 = new Random(); 
    int randomNumber2 = random2.Next(0, 300); 
    Random random3 = new Random(); 
    int randomNumber3 = random3.Next(0, 300); 
    Random random4 = new Random(); 
    int randomNumber4 = random4.Next(0, 300); 
    System.Drawing.Graphics g = this.CreateGraphics(); 
    Pen green = new Pen(Color.Green, 5); 
    g.DrawLine(green, new Point(randomNumber1, randomNumber2), 
         new Point(randomNumber3, randomNumber4)); 
} 

private void btndraw1_Click(object sender, EventArgs e) 
{ 
    Draw(); 
} 

Trả lời

48

Lý do điều này xảy ra là mỗi khi bạn làm một mới Random nó là được khởi tạo bằng đồng hồ. Vì vậy, trong một vòng lặp chặt chẽ (hoặc nhiều cuộc gọi một sau khi khác) bạn nhận được cùng một giá trị nhiều lần kể từ khi tất cả những biến ngẫu nhiên được khởi tạo với cùng một hạt giống.

Để giải quyết điều này: Chỉ tạo một biến ngẫu nhiên, tốt nhất là bên ngoài hàm của bạn và chỉ sử dụng một phiên bản đó.

Random random1 = new Random(); 
private void Draw() 
{ 
    int randomNumber1 = random1.Next(0, 300); 
    int randomNumber2 = random1.Next(0, 300); 
    int randomNumber3 = random1.Next(0, 300); 
    int randomNumber4 = random1.Next(0, 300); 
    System.Drawing.Graphics g = this.CreateGraphics(); 
    Pen green = new Pen(Color.Green, 5); 
    g.DrawLine(green, new Point(randomNumber1, randomNumber2), new Point(randomNumber3, randomNumber4)); 
} 
+3

+1 cho bên ngoài phương pháp. – keyboardP

9

Đơn giản chỉ cần sử dụng cùng một ví dụ:

Random random = new Random(); 
int randomNumber1 = random.Next(0, 300); 
int randomNumber2 = random.Next(0, 300); 
//... 

số ngẫu nhiên trong lập trình là không thực sự ngẫu nhiên; chúng được dựa trên một số hạt giống độc đáo được lấy và thao tác để tạo ra những gì dường như là tập hợp các số ngẫu nhiên. Sử dụng cùng một hạt giống sẽ dẫn đến cùng một tập hợp các số.

Hàm khởi tạo mặc định của lớp Random đang sử dụng số mili giây trôi qua kể từ khi hệ thống bắt đầu làm hạt giống, vì vậy điều thực sự xảy ra là cùng một hạt giống đã được sử dụng.

Thực sự không có lý do gì để tạo nhiều hơn một lần ví dụ Random; cá thể đơn sẽ tạo ra các tập số ngẫu nhiên trên mỗi lần thực thi mã.

Để chứng minh tuyên bố trên của tôi về hạt giống mặc định, tôi đã sử dụng phản ánh:

// System.Random 
/// <summary>Initializes a new instance of the <see cref="T:System.Random" /> class, using a time-dependent default seed value.</summary> 
public Random() : this(Environment.TickCount) 
{ 
} 

Environment.TickCount:

// System.Environment 
/// <summary>Gets the number of milliseconds elapsed since the system started.</summary> 
/// <returns>A 32-bit signed integer containing the amount of time in milliseconds that has passed since the last time the computer was started.</returns> 
/// <filterpriority>1</filterpriority> 
public static extern int TickCount 
{ 
    [SecuritySafeCritical] 
    [MethodImpl(MethodImplOptions.InternalCall)] 
    get; 
} 
+1

Chỉ ... làm sao bạn biết rằng 'Random' sử dụng kỷ nguyên Unix? Các [tài liệu] (http://msdn.microsoft.com/en-us/library/h343ddh9.aspx) chỉ nói rằng nó 'bắt nguồn từ đồng hồ hệ thống' nhưng không bao giờ đề cập đến việc thực hiện thực tế. –

+0

@Alvin điểm tốt! Đó chỉ là những gì tôi luôn nghĩ **, là điều hợp lý nhất theo ý kiến ​​của tôi. Sử dụng ILSpy tôi phát hiện ra rằng tôi đã sai đồng bằng và hạt giống thực là số mili giây kể từ khi hệ thống được bắt đầu. –

3

Bạn chỉ cần một phiên bản của lớp Random.

private void Draw() 
    { 
     Random random1 = new Random(); 
     int randomNumber1 = random1.Next(0, 300); 

     int randomNumber2 = random1.Next(0, 300); 

     int randomNumber3 = random1.Next(0, 300); 

     int randomNumber4 = random1.Next(0, 300); 

     System.Drawing.Graphics g = this.CreateGraphics(); 
     Pen green = new Pen(Color.Green, 5); 
     g.DrawLine(green, new Point(randomNumber1, randomNumber2), new Point(randomNumber3, randomNumber4)); 
    } 


    private void btndraw1_Click(object sender, EventArgs e) 
    { 
     Draw(); 
    } 
3
private static readonly Random Random1 = new Random(); 

    private void Draw() 
    { 

     int randomNumber1 = Random1.Next(0, 300); 
     int randomNumber2 = Random1.Next(0, 300); 
     int randomNumber3 = Random1.Next(0, 300); 
     int randomNumber4 = Random1.Next(0, 300); 
     System.Drawing.Graphics g = this.CreateGraphics(); 
     Pen green = new Pen(Color.Green, 5); 
     g.DrawLine(green, new Point(randomNumber1, randomNumber2), new Point(randomNumber3, randomNumber4)); 
    } 


    private void btndraw1_Click(object sender, EventArgs e) 
    { 
     Draw(); 
    } 
3

Bạn không nên tạo một Random đối tượng mới cho mỗi số. Thay vào đó, hãy sử dụng cùng một đối tượng:

Random r = new Random(); 

private void Draw() 
{ 
    // Create 4 random numbers 
    int[] numbers = Enumerable.Range(0, 4).Select(x => r.Next(0, 300)).ToArray(); 

    System.Drawing.Graphics g = this.CreateGraphics(); 
    Pen green = new Pen(Color.Green, 5); 
    g.DrawLine(green, new Point(numbers[0], numbers[1]), 
         new Point(numbers[2], numbers[3])); 
} 
-4

Giá trị hạt giống ngẫu nhiên là giá trị ngày bạn có thể sử dụng giá trị ngày làm hạt giống và nó sẽ hoạt động.

private void Draw() 
    { 
     Random random1 = new Random(unchecked((int)DateTime.Now.Ticks << (int)100)); 
     int randomNumber1 = random1.Next(0, 300); 
     Random random2 = new Random(unchecked((int)DateTime.Now.Ticks << (int)200)); 
     int randomNumber2 = random2.Next(0, 300); 
     Random random3 = new Random(unchecked((int)DateTime.Now.Ticks << (int)300)); 
     int randomNumber3 = random3.Next(0, 300); 
     Random random4 = new Random(unchecked((int)DateTime.Now.Ticks << (int)400)); 
     int randomNumber4 = random4.Next(0, 300); 
     System.Drawing.Graphics g = this.CreateGraphics(); 
     Pen green = new Pen(Color.Green, 5); 
     g.DrawLine(green, new Point(randomNumber1, randomNumber2), new Point(randomNumber3, randomNumber4)); 
    } 


private void btndraw1_Click(object sender, EventArgs e) 
{ 
    Draw(); 
} 
+1

Tại sao bạn cần giá trị giống như giá trị bạn đã sử dụng? Random() sử dụng một cái gì đó như thế theo mặc định. –

+0

Đó là sự thật nhưng chúng luôn luôn có xu hướng tạo ra cùng một số lượng gửi nó là an toàn hơn để thêm một số hạt giống duy nhất mà sẽ đảm bảo số ngẫu nhiên được tạo ra cho các đối tượng lớp ngẫu nhiên khác nhau. – Nikshep

+0

@Nikshep Nó vẫn là một giải pháp. Điều gì sẽ xảy ra nếu bạn có các hàm khác nhau tạo 'Ngẫu nhiên'? Bạn sẽ giữ một kiểm đếm? Nếu vậy, tại sao không có một ngẫu nhiên tĩnh anyway? Và bạn nhận ra rằng việc chuyển đổi 'Ticks' còn lại bởi 100 - 400 địa điểm là vô lý, phải không? – antonijn

3

Trình tạo số ngẫu nhiên (RNG) không thực sự tạo ra các số ngẫu nhiên. Thay vào đó, nó sử dụng một thuật toán để xác định một chuỗi các số, dường như là ngẫu nhiên. Trình tự này phụ thuộc vào seed được chạy qua thuật toán đã nói tại thời điểm bạn tạo RNG.

Theo mặc định, RNG được tạo bằng cách sử dụng đồng hồ của hệ thống làm hạt giống, vì đồng hồ thường thay đổi mỗi khi chương trình chạy, khiến cho việc dự đoán chuỗi "ngẫu nhiên" vô cùng khó khăn.

Trong trường hợp của bạn, rất có thể, đồng hồ không thay đổi giữa việc tạo đối tượng ngẫu nhiên và đối tượng khác; có thể là do sắp xếp lại lệnh của CPU.

Như Blachshma nói, tốt nhất là chỉ tạo một đối tượng ngẫu nhiên duy nhất và chỉ sử dụng đối tượng đó.

public static Random MyRNG = new Random(); // create a single static random object, that you can use across all classes 
private void Draw() 
{ 
    randomNumber1 = MyRNG.Next(0, 300); 
    randomNumber2 = MyRNG.Next(0, 300); 
    // and so forth 
} 

Hãy nhớ rằng bất kỳ trường hợp System.Random không đảm bảo được thread-safe, có nghĩa là nếu bạn có kế hoạch có nhiều bài chia sẻ đối tượng ngẫu nhiên tương tự, bạn phải khóa nó.

lock (MyRNG) 
{ 
    randomNumber = MyRNG.Next(0, 300); 
} 

Không làm như vậy có thể làm hỏng đối tượng ngẫu nhiên của bạn, dẫn đến kết quả cuộc gọi chỉ trả về 0.

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