首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

C# 隨機數生成最佳實踐:擴充方法實現指定範圍 Double 隨機值

     編輯:Java 相關知識

前言

.NET 標準庫 Random.NextDouble() 僅返回 [0.0, 1.0) 區間值,實際開發中頻繁需要「指定範圍的 double 隨機數」。本文提供一個線程安全、高效能、語義清晰的擴充方法實現,並解析其數學原理、執行緒安全陷阱與測試驗證方法。

一、核心實現:擴充方法 NextDouble(min, max)

using System;

public static class RandomExtensions
{
    /// 
    /// 返回 [minValue, maxValue) 區間的雙精度隨機數。
    /// 
    /// Random 實例(不可為 null)
    /// 包含下界
    /// 不包含上界
    /// 均勻分佈的隨機 double
    /// random 為 null
    /// minValue >= maxValue
    public static double NextDouble(this Random random, double minValue, double maxValue)
    {
        if (random == null)
            throw new ArgumentNullException(nameof(random));
        if (minValue >= maxValue)
            throw new ArgumentException("minValue 必須小於 maxValue", nameof(minValue));

        // 數學原理:[0,1) * (max-min) + min = [min, max)
        return random.NextDouble() * (maxValue - minValue) + minValue;
    }
}

二、數學原理與邊界分析

  • Random.NextDouble() 返回 [0.0, 1.0)(含 0,不含 1)。
  • 乘以 (max - min) → [0, max-min)。
  • 加 min → [min, max)。
  • 邊界:理論上可取到 min,永遠取不到 max(除非浮點誤差導致極小機率等於 max,可接受)。

三、執行緒安全:Random 不是線程安全的

方案適用場景效能代碼示例
ThreadLocal<Random>高併發、無共享狀態極高(無鎖)ThreadLocal<Random> local = ThreadLocal.withInitial(Random::new); local.Value.NextDouble(1,5);
lock (random)低併發、legacy 代碼中(鎖爭用)lock(rng) { return rng.NextDouble(min,max); }
Random.Shared (.NET 6+)全域單例、線程安全高(內部無鎖實現)Random.Shared.NextDouble(min,max);
ThreadStatic Random老專案相容高[ThreadStatic] static Random tls; if(tls==null) tls=new Random();

建議:.NET 6+ 直接用 Random.Shared;舊版本用 ThreadLocal<Random>。**嚴禁多線程直接共用單一 Random 實例**(內部種子更新非原子,會導致返回 0 或效能崩塌)。

四、單元測試驗證分佈均勻性

[TestClass]
public class RandomExtensionsTests
{
    [TestMethod]
    public void NextDouble_Range_UniformDistribution()
    {
        const int N = 1_000_000;
        var rng = new Random(42); // 固定種子可復現
        double min = 1.5, max = 2.5;
        double sum = 0;
        int outOfRange = 0;

        for (int i = 0; i < N; i++)
        {
            double v = rng.NextDouble(min, max);
            if (v < min || v >= max) outOfRange++;
            sum += v;
        }

        Assert.AreEqual(0, outOfRange, "所有值必須在 [min, max) 區間內");
        double mean = sum / N;
        double expectedMean = (min + max) / 2.0; // 2.0
        Assert.IsTrue(Math.Abs(mean - expectedMean) < 0.005, $"均值應接近 {expectedMean},實際 {mean}");
    }

    [TestMethod]
    [ExpectedException(typeof(ArgumentNullException))]
    public void NextDouble_NullRandom_Throws() => ((Random)null).NextDouble(0, 1);

    [TestMethod]
    [ExpectedException(typeof(ArgumentException))]
    public void NextDouble_MinGtMax_Throws() => new Random().NextDouble(5, 1);
}

五、進階:加密級隨機數 RandomNumberGenerator

若用於 Token、Session ID、加密鹽值,**必須用密碼學安全隨機源**:

using System.Security.Cryptography;

public static class SecureRandomExtensions
{
    public static double NextDouble(double min, double max)
    {
        if (min >= max) throw new ArgumentException();
        // 取 8 字節 → ulong → [0, 2^64-1] → 除以 2^64 得 [0,1)
        Span bytes = stackalloc byte[8];
        RandomNumberGenerator.Fill(bytes);
        ulong rand = BinaryPrimitives.ReadUInt64LittleEndian(bytes);
        double unit = rand / (double)ulong.MaxValue; // 約 [0,1)
        return unit * (max - min) + min;
    }
}

六、總結

  • 擴充方法 NextDouble(min, max):語義清晰、可測試、可復用。
  • 線程安全:.NET 6+ 用 Random.Shared,舊版用 ThreadLocal<Random>,**禁用共享實例**。
  • 驗證分佈:單元測試覆蓋區間邊界、均值、方差。
  • 安全場景:改用 RandomNumberGenerator 實現加密級隨機。

將擴充方法納入團隊共用庫,下次需要「1.5 到 2.5 之間的隨機延遲」、「模擬溫度傳感器 20.0~30.0 度」時,一行代碼搞定——規範、安全、可維護。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved