2010-01-04 68 views
5

Lưu ý: Vì lý do ngắn gọn, sau đây sẽ không phân biệt giữa ngẫu nhiên và giả ngẫu nhiên. Ngoài ra, trong bối cảnh này, chế nghĩa giữa phút nhất định và tối đa các giá trị)Tạo các giá trị giới hạn ngẫu nhiên (giả) của (U) Int64 và số thập phân

Lớp System.Random cung cấp thế hệ ngẫu nhiên của số nguyên, đôi và mảng byte. Sử dụng Random.Next, người ta có thể dễ dàng tạo ra các giá trị ràng buộc ngẫu nhiên của kiểu Boolean, Char, (S) Byte, (U) Int16, (U) Int32. Sử dụng Random.NextDouble(), người ta cũng có thể tạo ra các giá trị giới hạn của các kiểu Double và Single (theo như sự hiểu biết của tôi về kiểu này). Tạo chuỗi ngẫu nhiên (có chiều dài và bảng chữ cái đã cho) hasalsobeentackledbefore.

Xem xét các loại dữ liệu nguyên thủy còn lại (không bao gồm Đối tượng): Thập phân và (U) Int64. Thế hệ ngẫu nhiên của họ cũng đã được giải quyết (Decimal, (U)Int64 sử dụng Random.NextBytes()), nhưng không phải khi bị hạn chế. Từ chối lấy mẫu (tức là vòng lặp cho đến khi giá trị được tạo ra là phạm vi mong muốn) về mặt lý thuyết có thể được sử dụng, nhưng rõ ràng đây không phải là một giải pháp thực tế. Việc chuẩn hóa NextDouble() sẽ không hoạt động do không có đủ số chữ số đáng kể.

Nói tóm lại, tôi yêu cầu cho việc thực hiện đúng các chức năng sau:

long NextLong(long min, long max) 
long NextDecimal(decimal min, decimal max) 

Lưu ý rằng, kể từ khi System.DateTime được dựa trên một ulong, chức năng đầu tiên sẽ cho phép hệ ràng buộc ngẫu nhiên của cấu trúc như tốt (tương tự như here, chỉ trong ve thay vì phút).

Trả lời

6

Giả sử bạn biết cách tạo N bit ngẫu nhiên. Điều này khá dễ thực hiện bằng cách sử dụng NextBytes hoặc gọi lặp lại đến Random.Next với giới hạn thích hợp.

Để tạo một khoảng thời gian dài/dài trong phạm vi bên phải, hãy tìm ra phạm vi rộng bao nhiêu và cần bao nhiêu bit để biểu thị nó. Sau đó, bạn có thể sử dụng lấy mẫu từ chối sẽ ở mức thấp nhất từ chối một nửa giá trị được tạo (ví dụ: nếu bạn muốn giá trị trong phạm vi [0, 128], nghĩa là bạn sẽ tạo [0, 255] nhiều lần). Nếu bạn muốn một phạm vi khác không dựa trên, chỉ cần làm việc ra kích thước của phạm vi, tạo ra một giá trị ngẫu nhiên trong [0, kích thước) và sau đó thêm cơ sở.

Tạo một số thập phân ngẫu nhiên là khó khăn hơn, tôi tin rằng - ngoài bất cứ thứ gì khác, bạn phải chỉ định phân phối bạn muốn.

+0

Cảm ơn bạn đã hướng dẫn! +1 –

+0

Thực sự có khó không? Tôi phải thiếu một cái gì đó, vì suy nghĩ đầu tiên của tôi là sử dụng 'Random.NextBytes' và sử dụng các hoạt động bitwise để áp dụng chúng vào các bit thập phân thích hợp để lấy giá trị từ 0 đến 1. –

9

Điều này sẽ thực hiện. Đối với thập phân tôi sử dụng phương pháp ban đầu của Jon Skeet để tạo ngẫu nhiên decimal s (không có ràng buộc). Đối với long Tôi đã cung cấp một phương pháp để tạo ngẫu nhiên không tiêu cực long s sau đó được sử dụng để tạo ra một giá trị trong phạm vi ngẫu nhiên.

Lưu ý rằng đối với decimal, phân phối kết quả không phải là phân bố đồng đều trên [minValue, maxValue]. Nó chỉ đơn thuần là thống nhất trên tất cả các biểu diễn bit của các số thập phân nằm trong phạm vi [minValue, maxValue]. Tôi không thấy một cách dễ dàng xung quanh điều này mà không sử dụng lấy mẫu từ chối.

Đối với long phân phối kết quả là thống nhất trên [minValue, maxValue).

static class RandomExtensions { 
    static int NextInt32(this Random rg) { 
     unchecked { 
      int firstBits = rg.Next(0, 1 << 4) << 28; 
      int lastBits = rg.Next(0, 1 << 28); 
      return firstBits | lastBits; 
     } 
    } 

    public static decimal NextDecimal(this Random rg) { 
     bool sign = rg.Next(2) == 1; 
     return rg.NextDecimal(sign); 
    } 

    static decimal NextDecimal(this Random rg, bool sign) { 
     byte scale = (byte)rg.Next(29); 
     return new decimal(rg.NextInt32(), 
          rg.NextInt32(), 
          rg.NextInt32(), 
          sign, 
          scale); 
    } 

    static decimal NextNonNegativeDecimal(this Random rg) { 
     return rg.NextDecimal(false); 
    } 

    public static decimal NextDecimal(this Random rg, decimal maxValue) { 
     return (rg.NextNonNegativeDecimal()/Decimal.MaxValue) * maxValue; ; 
    } 

    public static decimal NextDecimal(this Random rg, decimal minValue, decimal maxValue) { 
     if (minValue >= maxValue) { 
      throw new InvalidOperationException(); 
     } 
     decimal range = maxValue - minValue; 
     return rg.NextDecimal(range) + minValue; 
    } 

    static long NextNonNegativeLong(this Random rg) { 
     byte[] bytes = new byte[sizeof(long)]; 
     rg.NextBytes(bytes); 
     // strip out the sign bit 
     bytes[7] = (byte)(bytes[7] & 0x7f); 
     return BitConverter.ToInt64(bytes, 0); 
    } 

    public static long NextLong(this Random rg, long maxValue) { 
     return (long)((rg.NextNonNegativeLong()/(double)Int64.MaxValue) * maxValue); 
    } 

    public static long NextLong(this Random rg, long minValue, long maxValue) { 
     if (minValue >= maxValue) { 
      throw new InvalidOperationException(); 
     } 
     long range = maxValue - minValue; 
     return rg.NextLong(range) + minValue; 
    } 
} 
+0

Cảm ơn, đây là một đoạn trích tuyệt vời (+1) - một vài câu hỏi mặc dù: 1. Bạn sẽ không mất thông tin trong rg.NextNonNegativeLong()/(đôi) Int64.MaxValue? Các diễn viên mất độ chính xác và phân chia có thể không được đại diện đầy đủ - sẽ không những yếu tố ảnh hưởng đến tính đồng nhất và vào tài sản? Điều tương tự liên quan đến NextNonNegativeDecimal()/Decimal.MaxValue 2. Trong NextInt32, có bất kỳ lý do 4 và 28 nào được chọn không? bất kỳ hai số dương nào cộng thêm 32 (ví dụ: 16 + 16) sẽ hoạt động đúng không? Cảm ơn! –

+1

Mã này không thể cung cấp ký dài. Gọi '.NextLong (long.MinValue, long.MaxValue)' luôn trả về '-9223372036854775808'. –

+0

NextDecimal (decimal.MinValue, 5) sẽ ném OverflowException – Danil

0

Dựa trên phương pháp Jon Skeet, đây là đâm của tôi lúc đó:

public static long NextLong(this Random rnd, long min, long max) 
{ 
    if (max <= min) 
    { 
     throw new Exception("Min must be less than max."); 
    } 

    long dif = max - min; 

    var bytes = new byte[8]; 
    rnd.NextBytes(bytes); 
    bytes[7] &= 0x7f; //strip sign bit 

    long posNum = BitConverter.ToInt64(bytes, 0); 
    while (posNum > dif) 
    { 
     posNum >>= 1; 
    } 

    return min + posNum; 
} 

Hãy cho tôi biết nếu bạn thấy bất kỳ lỗi nào.

+0

Giảm một giá trị rất lớn bằng cách chia cho nó bằng một sức mạnh của 2 co lại số theo kiểu có thể đoán trước được. Nó sẽ là tốt hơn thay vì để loại bỏ bit THÊM đáng kể, không ít hơn. Hãy thử bit-shifting 1 mặt nạ bên phải cho đến khi bitwise AND của giá trị và mặt nạ nằm trong phạm vi. Bạn nhận được phân phối tốt hơn các giá trị ngẫu nhiên. – KeithS

-2
long posNum = BitConverter.ToInt64(Guid.NewGuid().ToByteArray(), 0); 


use this instead of NextBytes 
+0

-1. GUID không hoàn toàn ngẫu nhiên. Chúng không nên được sử dụng thay cho trình tạo số giả ngẫu nhiên. –

0

Tôi đã tìm cách tạo các giá trị 64 bit trong một phạm vi tùy ý. Các câu trả lời khác không thể tạo ra một số ngẫu nhiên khi được cung cấp một số phạm vi nhất định (ví dụ: long.MinValue to long.MaxValue). Dưới đây là phiên bản của tôi mà dường như để giải quyết vấn đề:

public static long NextInt64(this Random random, long minValue, long maxValue) 
{ 
    Contract.Requires(random != null); 
    Contract.Requires(minValue <= maxValue); 
    Contract.Ensures(Contract.Result<long>() >= minValue && 
        Contract.Result<long>() < maxValue); 

    return (long)(minValue + (random.NextUInt64() % ((decimal)maxValue - minValue))); 
} 

Nó sử dụng phương pháp mở rộng sau:

public static ulong NextUInt64(this Random random) 
{ 
    Contract.Requires(random != null); 

    return BitConverter.ToUInt64(random.NextBytes(8), 0); 
} 

public static byte[] NextBytes(this Random random, int byteCount) 
{ 
    Contract.Requires(random != null); 
    Contract.Requires(byteCount > 0); 
    Contract.Ensures(Contract.Result<byte[]>() != null && 
        Contract.Result<byte[]>().Length == byteCount); 

    var buffer = new byte[byteCount]; 
    random.NextBytes(buffer); 
    return buffer; 
} 

Sự phân bố không phải là hoàn hảo ngay cả khi kích thước của dãy yêu cầu không phải là một ước sạch 2^64, nhưng ít nhất nó cung cấp một số ngẫu nhiên trong phạm vi yêu cầu cho bất kỳ phạm vi nhất định nào.

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