2010-07-29 34 views
40

Cách tốt nhất để tạo ra một phao ngẫu nhiên trong C# là gì?Cách tốt nhất để tạo ra một phao ngẫu nhiên trong C#

Cập nhật: Tôi muốn số dấu phẩy động ngẫu nhiên từ float.Minvalue thành float.Maxvalue. Tôi đang sử dụng những con số này trong thử nghiệm đơn vị của một số phương pháp toán học.

+20

4.0 - được chọn ngẫu nhiên bằng cuộn xúc xắc nổi điểm công bằng. –

+5

@ JesseC.Slicer Bạn quên cung cấp liên kết: http://xkcd.com/221/ – jpmc26

Trả lời

48

Cách tiếp cận tốt nhất, không có giá trị điên cuồng, distributed with respect to the representable intervals on the floating-point number line (gỡ bỏ "đồng phục" như đối với một số dòng liên tục với nó là decidedly không đồng nhất):

static float NextFloat(Random random) 
{ 
    double mantissa = (random.NextDouble() * 2.0) - 1.0; 
    double exponent = Math.Pow(2.0, random.Next(-126, 128)); 
    return (float)(mantissa * exponent); 
} 

một cách tiếp cận này sẽ cung cấp cho bạn một số giá trị điên cuồng (phân bố đồng đều các mẫu bit), có khả năng hữu ích cho fuzzing:

static float NextFloat(Random random) 
{ 
    var buffer = new byte[4]; 
    random.NextBytes(buffer); 
    return BitConverter.ToSingle(buffer,0); 
} 

cách tiếp cận hữu ích Least:

static float NextFloat(Random random) 
{ 
    // Not a uniform distribution w.r.t. the binary floating-point number line 
    // which makes sense given that NextDouble is uniform from 0.0 to 1.0. 
    // Uniform w.r.t. a continuous number line. 
    // 
    // The range produced by this method is 6.8e38. 
    // 
    // Therefore if NextDouble produces values in the range of 0.0 to 0.1 
    // 10% of the time, we will only produce numbers less than 1e38 about 
    // 10% of the time, which does not make sense. 
    var result = (random.NextDouble() 
        * (Single.MaxValue - (double)Single.MinValue)) 
        + Single.MinValue; 
    return (float)result; 
} 

Floating điểm dòng số từ: Intel Architecture Software Developer's Manual Volume 1: Basic Architecture. Trục Y là logarit (cơ sở 2) vì liên tiếp số dấu chấm động nhị phân không khác nhau theo đường thẳng.

Comparison of distributions, logarithmic Y-axis

+0

Có vẻ như không phải là một phương pháp BitConverter.GetSingle. Bạn có nghĩa là ToSingle? – KrisTrip

+0

Điều đó có nghĩa là ... – user7116

+1

Sử dụng các byte ngẫu nhiên có thể dễ dàng kết thúc bằng giá trị NaN và phân phối có thể không đồng đều. (Tôi không muốn dự đoán phân phối.) –

20

Bất kỳ lý do nào không sử dụng Random.NextDouble và sau đó truyền tới float? Điều đó sẽ cung cấp cho bạn một dấu phẩy giữa 0 và 1.

Nếu bạn muốn có một hình thức "tốt nhất" khác, bạn cần phải chỉ định các yêu cầu của mình. Lưu ý rằng không nên sử dụng Random cho các vấn đề nhạy cảm như tài chính hoặc bảo mật - và bạn thường nên sử dụng lại một cá thể hiện có trong toàn bộ ứng dụng của mình hoặc một cho mỗi chủ đề (như Random không an toàn cho chủ đề).

EDIT: Như đã đề cập trong các ý kiến, để chuyển đổi này vào một loạt các float.MinValue, float.MaxValue:

// Perform arithmetic in double type to avoid overflowing 
double range = (double) float.MaxValue - (double) float.MinValue; 
double sample = rng.NextDouble(); 
double scaled = (sample * range) + float.MinValue; 
float f = (float) scaled; 

EDIT: Bây giờ bạn đã đề cập rằng đây là để thử nghiệm đơn vị, tôi không chắc chắn nó là một cách tiếp cận lý tưởng. Có thể bạn nên thử nghiệm với các giá trị cụ thể - đảm bảo bạn thử nghiệm với các mẫu trong từng danh mục có liên quan - số vô hạn, số NaN, số không chuẩn, số rất lớn, số không, v.v.

+0

Tôi nghĩ NextDouble chỉ cung cấp các giá trị từ 0.0 đến 1.0 và tôi muốn nhiều hơn thế (như từ float.MinValue tới float.MaxValue). Đoán tôi nên có quy định :) – KrisTrip

+3

Chỉ nhân nó bằng (MaxValue-MinValue) và thêm MinValue cho nó? Vì vậy, một cái gì đó như Random.NextDouble * (float.MaxValue-float.MinValue) + float.MinValue – Xzhsh

+0

Việc phân phối các giá trị mà sản xuất là lưỡng tính, tôi tin rằng nó đã làm với các giá trị lớn như thế nào trong phạm vi. – user7116

1

Tôi mất một cách tiếp cận hơi khác so với những người khác

static float NextFloat(Random random) 
{ 
    double val = random.NextDouble(); // range 0.0 to 1.0 
    val -= 0.5; // expected range now -0.5 to +0.5 
    val *= 2; // expected range now -1.0 to +1.0 
    return float.MaxValue * (float)val; 
} 

Các ý kiến ​​giải thích những gì tôi đang làm. Nhận số double tiếp theo, chuyển đổi số đó thành giá trị giữa -1 và 1 rồi nhân với giá trị float.MaxValue.

0

giải pháp khác là để làm điều này:

static float NextFloat(Random random) 
{ 
    float f; 
    do 
    { 
     byte[] bytes = new byte[4]; 
     random.NextBytes(bytes); 
     f = BitConverter.ToSingle(bytes, 0); 
    } 
    while (float.IsInfinity(f) || float.IsNaN(f)); 
    return f; 
} 
5

hơn Một phiên bản ... (Tôi nghĩ cái này là khá tốt)

static float NextFloat(Random random) 
{ 
    (float)(float.MaxValue * 2.0 * (rand.NextDouble()-0.5)); 
} 

//inline version 
float myVal = (float)(float.MaxValue * 2.0 * (rand.NextDouble()-0.5)); 

Tôi nghĩ rằng đây ...

  • là 2 nhanh nhất (xem tiêu chuẩn)
  • được phân bố đều

Và Một phiên bản thêm ... (không phải là tốt nhưng việc đăng nào)

static float NextFloat(Random random) 
{ 
    return float.MaxValue * ((rand.Next()/1073741824.0f) - 1.0f); 
} 

//inline version 
float myVal = (float.MaxValue * ((rand.Next()/1073741824.0f) - 1.0f)); 

Tôi nghĩ rằng điều này ...

  • là nhanh nhất (xem điểm chuẩn)
  • được phân phối đồng đều vì Next() là giá trị ngẫu nhiên 31 bit, nó sẽ chỉ trả về 2^31 giá trị. (50% giá trị hàng xóm sẽ có cùng giá trị)

nghiệm của hầu hết các chức năng trên trang này: (i7, phát hành, mà không cần gỡ lỗi, 2^28 vòng)

Sunsetquest1: min: 3.402823E+38 max: -3.402823E+38 time: 3096ms 
SimonMourier: min: 3.402823E+38 max: -3.402819E+38 time: 14473ms 
AnthonyPegram:min: 3.402823E+38 max: -3.402823E+38 time: 3191ms 
JonSkeet:  min: 3.402823E+38 max: -3.402823E+38 time: 3186ms 
Sixlettervar: min: 1.701405E+38 max: -1.701410E+38 time: 19653ms 
Sunsetquest2: min: 3.402823E+38 max: -3.402823E+38 time: 2930ms 
+1

Bạn có chắc chắn rằng nó sẽ nhanh hơn vì nó là một sức mạnh của hai? Tôi sẽ giả định nó sẽ được ngầm đúc đến một phao * (mà sẽ không đại diện cho giá trị chính xác, sau đó sử dụng phân chia nổi thường xuyên). * – ideasman42

+0

Cảm ơn bạn đã nhận thấy rằng, đó là một bắt tốt! Bạn nói đúng là nó đã được chuyển đổi thành phao trước để trình biên dịch không thể sử dụng lệnh thay đổi. Tôi gỡ bỏ "nhanh hơn vì nó là một sức mạnh của hai". Như bạn có thể nói tôi đã thực hiện một số thay đổi khác bao gồm thêm một chức năng mới và cũng làm tiêu chuẩn. – Sunsetquest

0

Đây là một cách khác mà tôi đã đưa ra: Giả sử bạn muốn lấy một điểm nổi giữa 5,5 và 7, với 3 số thập phân.

float myFloat; 
int myInt; 
System.Random rnd = new System.Random(); 

void GenerateFloat() 
{ 
myInt = rnd.Next(1, 2000); 
myFloat = (myInt/1000) + 5.5f; 
} 

Bằng cách đó bạn sẽ luôn có được một số lượng lớn hơn 5,5 và một số nhỏ hơn 7.

0

Tôi thích sử dụng đoạn mã sau để tạo ra một số thập phân lên đến nắm tay dấu thập phân. bạn có thể sao chép dán dòng thứ 3 để thêm số sau dấu thập phân bằng cách nối số đó vào chuỗi "kết hợp". Bạn có thể đặt giá trị tối thiểu và tối đa bằng cách thay đổi giá trị 0 và 9 thành giá trị ưu tiên của bạn.

Random r = new Random(); 
string beforePoint = r.Next(0, 9).ToString();//number before decimal point 
string afterPoint = r.Next(0,9).ToString();//1st decimal point 
//string secondDP = r.Next(0, 9).ToString();//2nd decimal point 
string combined = beforePoint+"."+afterPoint; 
decimalNumber= float.Parse(combined); 
Console.WriteLine(decimalNumber); 

Tôi hy vọng nó giúp ích cho bạn.

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