2011-08-31 56 views
5

Tôi biết rằng C# lớp ngẫu nhiên không làm cho "true ngẫu nhiên" con số, nhưng tôi đến với một vấn đề với mã này:số C# ngẫu nhiên không là "ngẫu nhiên"

public void autoAttack(enemy theEnemy) 
    { 
     //Gets the random number 
     float damage = randomNumber((int)(strength * 1.5), (int)(strength * 2.5)); 

     //Reduces the damage by the enemy's armor 
     damage *= (100/(100 + theEnemy.armor)); 

     //Tells the user how much damage they did 
     Console.WriteLine("You attack the enemy for {0} damage", (int)damage); 

     //Deals the actual damage 
     theEnemy.health -= (int)damage; 

     //Tells the user how much health the enemy has left 
     Console.WriteLine("The enemy has {0} health left", theEnemy.health); 
    } 

tôi sau đó gọi hàm ở đây (tôi gọi nó là 5 lần vì lợi ích của kiểm tra nếu các con số là ngẫu nhiên):

 if (thePlayer.input == "fight") 
     { 
      Console.WriteLine("you want to fight"); 
      thePlayer.autoAttack(enemy1); 
      thePlayer.autoAttack(enemy1); 
      thePlayer.autoAttack(enemy1); 
     } 

Tuy nhiên, khi tôi kiểm tra đầu ra, tôi nhận được con số chính xác như nhau cho mỗi 3 chức năng cuộc gọi. Tuy nhiên, mỗi khi tôi chạy chương trình, tôi nhận được một số lượng khác nhau (mà lặp đi lặp lại 3 lần) như thế này:

You attack the enemy for 30 damage. 
The enemy has 70 health left. 

You attack the enemy for 30 damage. 
The enemy has 40 health left. 

You attack the enemy for 30 damage. 
The enemy has 10 health left. 

sau đó tôi sẽ xây dựng lại/debug/chạy chương trình một lần nữa, và có được một số lượng khác nhau thay vì 30 , nhưng nó sẽ lặp lại tất cả 3 lần.

Câu hỏi của tôi là: làm cách nào để đảm bảo nhận được số ngẫu nhiên khác mỗi khi tôi gọi hàm này? Tôi chỉ nhận được cùng một "ngẫu nhiên" số hơn và hơn nữa.

Đây là cuộc gọi lớp ngẫu nhiên mà tôi đã sử dụng:

private int randomNumber(int min, int max) 
    { 
     Random random = new Random(); 
     return random.Next(min, max); 
    } 
+4

Chức năng 'randomNumber' của bạn trông như thế nào? – Nija

+0

Bạn có thể xem [bài đăng này] (http://www.codeducky.org/random-numbers-c-net-primer/), thảo luận về vấn đề này cũng như các gotchas khác với lớp .NET Random – ChaseMedallion

Trả lời

26

tôi đoán là randomNumber tạo ra một thể hiện mới của Random mỗi lần ... từ đó tạo ra một bộ tạo số giả ngẫu nhiên mới dựa trên thời gian hiện tại ... không thay đổi thường xuyên như bạn nghĩ.

Đừng làm điều đó. Sử dụng cùng một trường hợp Random nhiều lần ... nhưng không "sửa" bằng cách tạo biến tĩnh Random. Điều đó sẽ không hoạt động tốt trong thời gian dài vì Random không an toàn cho chủ đề. Tất cả sẽ tốt đẹp khi thử nghiệm, sau đó bạn sẽ bí ẩn nhận được tất cả các số 0 sau khi bạn tình cờ gặp khó khăn với đồng thời: (

May mắn thay nó không quá khó để làm việc trên NET 4. bạn kết thúc với một trường hợp mới của Random mỗi thread

tôi đã viết một article on this very topic mà bạn có thể tìm thấy hữu ích, bao gồm cả mã này:.

using System; 
using System.Threading; 

public static class RandomProvider 
{  
    private static int seed = Environment.TickCount; 

    private static ThreadLocal<Random> randomWrapper = new ThreadLocal<Random> 
     (() => new Random(Interlocked.Increment(ref seed))); 

    public static Random GetThreadRandom() 
    { 
     return randomWrapper.Value; 
    } 
} 

Nếu bạn thay đổi new Random() cuộc gọi của bạn đến RandomProvider.GetThreadRandom() có thể sẽ làm mọi thứ bạn ne ed (một lần nữa, giả sử .NET 4). Điều đó không giải quyết được khả năng kiểm tra, nhưng từng bước một ...

+0

làm thế nào điều này có thể được sửa đổi để sử dụng một giá trị min và max như trong câu hỏi? – Julien

0

randomNumber là gì?

Thông thường một trình tạo số giả ngẫu nhiên được tạo hạt (với một điều liên quan đến thời gian hoặc một cái gì đó ngẫu nhiên như thời gian giữa hai lần nhấn phím hoặc gói mạng hoặc thứ gì đó).

Bạn không cho biết bạn đang sử dụng trình tạo nào và cũng không được tạo giống như thế nào.

+0

Tôi đã thêm lớp số ngẫu nhiên mà tôi đã sử dụng vào bài đăng gốc – Mento

7

Bạn không hiển thị mã cho chúng tôi randomNumber. Nếu có vẻ như bất kỳ điều gì giống như

private int randomNumber(int m, int n) { 
    Random rg = new Random(); 
    int y = rg.Next(); 
    int z = // some calculations using m and n 
    return z; 
} 

Vâng, có vấn đề của bạn.Nếu bạn tiếp tục tạo các phiên bản mới của Random, có thể đôi khi chúng sẽ có cùng một hạt giống (hạt giống mặc định là đồng hồ hệ thống có độ chính xác giới hạn; tạo chúng nhanh chóng và chúng có cùng hạt giống) và sau đó trình tự được tạo ra bởi máy phát sẽ luôn giống nhau.

Để khắc phục điều này, bạn phải nhanh chóng một thể hiện của Random một lần:

private readonly Random rg = new Random(); 
private int randomNumber(int m, int n) { 
    int y = this.rg.Next(); 
    int z = // some calculations using m and n 
    return z; 
} 

Và để làm sáng tỏ điểm khác, ngay cả khi bạn làm điều này, đầu ra từ Random vẫn không phải là "true" ngẫu nhiên. Nó chỉ là psuedorandom.

+1

+1 cho ví dụ. Tuy nhiên, nhiều khả năng nó sẽ * gần như luôn luôn * mang lại giá trị tương tự nếu được gọi trong kế tiếp rất ngắn .... ;-) "Giá trị mặc định của hạt giống được lấy từ đồng hồ hệ thống và có độ phân giải hữu hạn." –

+0

@pst, không, nó không mang lại giá trị tương tự cho điều đó. 'Seed' (giá trị ban đầu) phụ thuộc vào đồng hồ, nhưng trạng thái bên trong của nó thay đổi khi bạn sử dụng nó. –

+0

@ J-16 SDiZ: Tôi nghĩ ý nghĩa của pst là nếu bạn có một phương thức khởi tạo một thể hiện mới của 'Random' và trả về một giá trị ngẫu nhiên duy nhất, và gọi phương thức đó liên tục trong một vòng lặp, bạn sẽ thấy giá trị lặp đi lặp lại một vài lần khi hạt giống không thay đổi, và sau đó một giá trị lặp đi lặp lại một vài lần như hạt giống đã thay đổi từ đồng hồ thay đổi, và như vậy. – jason

0

nếu bạn tạo các số ngẫu nhiên trong vòng lặp, nó có thể sẽ không ngẫu nhiên. vì số ngẫu nhiên về cơ bản được tạo trong nội bộ trên hệ thống hiện tại. Vì vậy, hãy đặt mã này trong vòng lặp:

Thread.Sleep(10); 

Vì vậy, hệ thống sẽ chuyển sang chế độ ngủ trong 10 m giây. Và bạn sẽ nhận được số ngẫu nhiên mới. Giải pháp bảo đảm của nó. Nhưng điều này cũng sẽ ảnh hưởng đến hiệu suất của hệ thống.

+0

Trong sự bối rối của tôi, sử dụng Sleep là cách giải quyết thô lỗ của tôi khi tôi tiếp tục nhận được 2-3 giá trị lặp lại và bị bối rối. BTW, tôi vẫn nhận được lặp lại tại (100). Tại (500), nó trông ngẫu nhiên. Nhưng yeah, hiệu suất là obv khủng khiếp. @ Jason của câu trả lời ở trên là một sửa chữa tốt. – nanonerd

0

Khởi tạo đối tượng ngẫu nhiên bên ngoài phương thức. (Random random = new Random(); phải được viết trước phương thức)

Điều quan trọng là bạn hiểu rằng ngẫu nhiên không phải là really random.