System.Random 類別

本文提供此 API 參考文件的補充備註。

類別 Random 代表虛擬隨機數產生器,這是一種演算法,會產生符合特定隨機性統計需求的數位序列。

虛擬隨機數是從一組有限數字中選取的機率相等的。 選擇的數位不是完全隨機的,因為數學演算法用來選取它們,但它們足以隨機進行實際用途。 類別的實作 Random 是以 Donald E. Knuth 的減數隨機數產生器演算法的修改版本為基礎。 如需詳細資訊,請參閱 D. E. Knuth。 計算機程式設計藝術,第 2 卷:半數值演算法。 Addison-Wesley,雷丁,MA,第三版,1997年。

若要產生密碼編譯安全的隨機數,例如適合建立隨機密碼的隨機數位,請使用 RNGCryptoServiceProvider 類別或從 System.Security.Cryptography.RandomNumberGenerator衍生類別。

具現化隨機數產生器

您可以將種子值(虛擬隨機數產生演算法的起始值) Random 提供給類別建構函式,以具現化隨機數產生器。 您可以明確或隱含地提供種子值:

  • Random(Int32) 構函式會使用您提供的明確種子值。
  • Random() 構函式會使用預設種子值。 這是具現化隨機數產生器最常見的方式。

在 .NET Framework 中,默認種子值是時間相依的。 在 .NET Core 中,默認種子值是由線程靜態虛擬隨機數產生器所產生。

如果相同的種子用於不同的 Random 物件,它們會產生相同的隨機數序列。 這對於建立可處理隨機值的測試套件,或重新執行從隨機數位衍生其數據的遊戲很有用。 不過,請注意, Random 在以不同 .NET Framework 版本執行之進程中的物件,即使它們以相同的種子值具現化,仍可能會傳回隨機數的不同範圍。

若要產生不同的隨機數序列,您可以讓種子值時間相依,藉此產生具有每個新 實例 Random的不同數列。 參數化 Random(Int32) 建構函式可以根據目前時間的刻度數來取得 Int32 值,而無 Random() 參數建構函式則使用系統時鐘來產生其種子值。 不過,只有在 .NET Framework 上,因為時鐘具有有限的解析度,因此使用無參數建構函式在緊接續中建立不同的 Random 物件,會建立產生相同隨機數序列的隨機數產生器。 下列範例說明 .NET Framework 應用程式中的兩 Random 個物件如何連續具現化,產生相同的隨機數序列。 在大部分的 Windows 系統上, Random 彼此在 15 毫秒內建立的物件可能會有相同的種子值。

byte[] bytes1 = new byte[100];
byte[] bytes2 = new byte[100];
Random rnd1 = new Random();
Random rnd2 = new Random();

rnd1.NextBytes(bytes1);
rnd2.NextBytes(bytes2);

Console.WriteLine("First Series:");
for (int ctr = bytes1.GetLowerBound(0);
     ctr <= bytes1.GetUpperBound(0);
     ctr++) {
   Console.Write("{0, 5}", bytes1[ctr]);
   if ((ctr + 1) % 10 == 0) Console.WriteLine();
}

Console.WriteLine();

Console.WriteLine("Second Series:");
for (int ctr = bytes2.GetLowerBound(0);
     ctr <= bytes2.GetUpperBound(0);
     ctr++) {
   Console.Write("{0, 5}", bytes2[ctr]);
   if ((ctr + 1) % 10 == 0) Console.WriteLine();
}

// The example displays output like the following:
//       First Series:
//          97  129  149   54   22  208  120  105   68  177
//         113  214   30  172   74  218  116  230   89   18
//          12  112  130  105  116  180  190  200  187  120
//           7  198  233  158   58   51   50  170   98   23
//          21    1  113   74  146  245   34  255   96   24
//         232  255   23    9  167  240  255   44  194   98
//          18  175  173  204  169  171  236  127  114   23
//         167  202  132   65  253   11  254   56  214  127
//         145  191  104  163  143    7  174  224  247   73
//          52    6  231  255    5  101   83  165  160  231
//
//       Second Series:
//          97  129  149   54   22  208  120  105   68  177
//         113  214   30  172   74  218  116  230   89   18
//          12  112  130  105  116  180  190  200  187  120
//           7  198  233  158   58   51   50  170   98   23
//          21    1  113   74  146  245   34  255   96   24
//         232  255   23    9  167  240  255   44  194   98
//          18  175  173  204  169  171  236  127  114   23
//         167  202  132   65  253   11  254   56  214  127
//         145  191  104  163  143    7  174  224  247   73
//          52    6  231  255    5  101   83  165  160  231
let bytes1 = Array.zeroCreate 100
let bytes2 = Array.zeroCreate 100
let rnd1 = Random()
let rnd2 = Random()

rnd1.NextBytes bytes1 
rnd2.NextBytes bytes2 

printfn "First Series"
for i = bytes1.GetLowerBound 0 to bytes1.GetUpperBound 0 do
    printf "%5i" bytes1.[i]
    if (i + 1) % 10 = 0 then printfn ""

printfn ""

printfn "Second Series"
for i = bytes2.GetLowerBound 0 to bytes2.GetUpperBound 0 do
    printf "%5i" bytes2.[i]
    if (i + 1) % 10 = 0 then printfn ""

// The example displays output like the following:
//       First Series:
//          97  129  149   54   22  208  120  105   68  177
//         113  214   30  172   74  218  116  230   89   18
//          12  112  130  105  116  180  190  200  187  120
//           7  198  233  158   58   51   50  170   98   23
//          21    1  113   74  146  245   34  255   96   24
//         232  255   23    9  167  240  255   44  194   98
//          18  175  173  204  169  171  236  127  114   23
//         167  202  132   65  253   11  254   56  214  127
//         145  191  104  163  143    7  174  224  247   73
//          52    6  231  255    5  101   83  165  160  231
//
//       Second Series:
//          97  129  149   54   22  208  120  105   68  177
//         113  214   30  172   74  218  116  230   89   18
//          12  112  130  105  116  180  190  200  187  120
//           7  198  233  158   58   51   50  170   98   23
//          21    1  113   74  146  245   34  255   96   24
//         232  255   23    9  167  240  255   44  194   98
//          18  175  173  204  169  171  236  127  114   23
//         167  202  132   65  253   11  254   56  214  127
//         145  191  104  163  143    7  174  224  247   73
//          52    6  231  255    5  101   83  165  160  231
Module modMain

   Public Sub Main()
      Dim bytes1(99), bytes2(99) As Byte
      Dim rnd1 As New Random()
      Dim rnd2 As New Random()
      
      rnd1.NextBytes(bytes1)
      rnd2.NextBytes(bytes2)
      
      Console.WriteLine("First Series:")
      For ctr As Integer = bytes1.GetLowerBound(0) to bytes1.GetUpperBound(0)
         Console.Write("{0, 5}", bytes1(ctr))
         If (ctr + 1) Mod 10 = 0 Then Console.WriteLine()
      Next 
      Console.WriteLine()
      Console.WriteLine("Second Series:")        
      For ctr As Integer = bytes2.GetLowerBound(0) to bytes2.GetUpperBound(0)
         Console.Write("{0, 5}", bytes2(ctr))
         If (ctr + 1) Mod 10 = 0 Then Console.WriteLine()
      Next   
   End Sub
End Module
' The example displays output like the following:
'       First Series:
'          97  129  149   54   22  208  120  105   68  177
'         113  214   30  172   74  218  116  230   89   18
'          12  112  130  105  116  180  190  200  187  120
'           7  198  233  158   58   51   50  170   98   23
'          21    1  113   74  146  245   34  255   96   24
'         232  255   23    9  167  240  255   44  194   98
'          18  175  173  204  169  171  236  127  114   23
'         167  202  132   65  253   11  254   56  214  127
'         145  191  104  163  143    7  174  224  247   73
'          52    6  231  255    5  101   83  165  160  231
'       
'       Second Series:
'          97  129  149   54   22  208  120  105   68  177
'         113  214   30  172   74  218  116  230   89   18
'          12  112  130  105  116  180  190  200  187  120
'           7  198  233  158   58   51   50  170   98   23
'          21    1  113   74  146  245   34  255   96   24
'         232  255   23    9  167  240  255   44  194   98
'          18  175  173  204  169  171  236  127  114   23
'         167  202  132   65  253   11  254   56  214  127
'         145  191  104  163  143    7  174  224  247   73
'          52    6  231  255    5  101   83  165  160  231

若要避免這個問題,請建立單 Random 一物件,而不是多個物件。 請注意, Random .NET Core 中的 類別沒有這項限制。

避免多個具現化

在 .NET Framework 上,在緊密迴圈或快速連續初始化兩個隨機數產生器時,會建立兩個可以產生相同隨機數序列的隨機數產生器。 在大部分情況下,這不是開發人員的意圖,而且可能會導致效能問題,因為具現化和初始化隨機數產生器是一個相對昂貴的程式。

為了改善效能,以及避免不小心建立產生相同數值序列的個別隨機數產生器,建議您建立一個 Random 對象來產生一段時間的隨機數,而不是建立新的 Random 對象來產生一個隨機數。

不過,類別 Random 不是安全線程。 如果您從多個線程呼叫 Random 方法,請遵循下一節中討論的指導方針。

執行緒安全

我們建議您建立單Random一實例來產生應用程式所需的所有隨機數位,而不是具現化個別Random物件。 不過, Random 物件不是安全線程。 如果您的應用程式從多個線程呼叫 Random 方法,您必須使用同步處理物件,以確保一次只能有一個線程存取隨機數產生器。 如果您不確定 Random 物件是以安全線程的方式存取,則呼叫傳回隨機數的方法會傳回 0。

下列範例會使用 C# lock 語句、F# lock 函式和 Visual Basic SyncLock 語句,以確保 11 個線程會以安全線程的方式存取單一隨機數產生器。 每個線程會產生 200 萬個隨機數、計算產生的隨機數數目,並計算其總和,然後在完成執行時更新所有線程的總計。

using System;
using System.Threading;

public class Example13
{
    [ThreadStatic] static double previous = 0.0;
    [ThreadStatic] static int perThreadCtr = 0;
    [ThreadStatic] static double perThreadTotal = 0.0;
    static CancellationTokenSource source;
    static CountdownEvent countdown;
    static Object randLock, numericLock;
    static Random rand;
    double totalValue = 0.0;
    int totalCount = 0;

    public Example13()
    {
        rand = new Random();
        randLock = new Object();
        numericLock = new Object();
        countdown = new CountdownEvent(1);
        source = new CancellationTokenSource();
    }

    public static void Main()
    {
        Example13 ex = new Example13();
        Thread.CurrentThread.Name = "Main";
        ex.Execute();
    }

    private void Execute()
    {
        CancellationToken token = source.Token;

        for (int threads = 1; threads <= 10; threads++)
        {
            Thread newThread = new Thread(this.GetRandomNumbers);
            newThread.Name = threads.ToString();
            newThread.Start(token);
        }
        this.GetRandomNumbers(token);

        countdown.Signal();
        // Make sure all threads have finished.
        countdown.Wait();
        source.Dispose();

        Console.WriteLine("\nTotal random numbers generated: {0:N0}", totalCount);
        Console.WriteLine("Total sum of all random numbers: {0:N2}", totalValue);
        Console.WriteLine("Random number mean: {0:N4}", totalValue / totalCount);
    }

    private void GetRandomNumbers(Object o)
    {
        CancellationToken token = (CancellationToken)o;
        double result = 0.0;
        countdown.AddCount(1);

        try
        {
            for (int ctr = 0; ctr < 2000000; ctr++)
            {
                // Make sure there's no corruption of Random.
                token.ThrowIfCancellationRequested();

                lock (randLock)
                {
                    result = rand.NextDouble();
                }
                // Check for corruption of Random instance.
                if ((result == previous) && result == 0)
                {
                    source.Cancel();
                }
                else
                {
                    previous = result;
                }
                perThreadCtr++;
                perThreadTotal += result;
            }

            Console.WriteLine("Thread {0} finished execution.",
                              Thread.CurrentThread.Name);
            Console.WriteLine("Random numbers generated: {0:N0}", perThreadCtr);
            Console.WriteLine("Sum of random numbers: {0:N2}", perThreadTotal);
            Console.WriteLine("Random number mean: {0:N4}\n", perThreadTotal / perThreadCtr);

            // Update overall totals.
            lock (numericLock)
            {
                totalCount += perThreadCtr;
                totalValue += perThreadTotal;
            }
        }
        catch (OperationCanceledException e)
        {
            Console.WriteLine("Corruption in Thread {1}", e.GetType().Name, Thread.CurrentThread.Name);
        }
        finally
        {
            countdown.Signal();
        }
    }
}
// The example displays output like the following:
//       Thread 6 finished execution.
//       Random numbers generated: 2,000,000
//       Sum of random numbers: 1,000,491.05
//       Random number mean: 0.5002
//
//       Thread 10 finished execution.
//       Random numbers generated: 2,000,000
//       Sum of random numbers: 999,329.64
//       Random number mean: 0.4997
//
//       Thread 4 finished execution.
//       Random numbers generated: 2,000,000
//       Sum of random numbers: 1,000,166.89
//       Random number mean: 0.5001
//
//       Thread 8 finished execution.
//       Random numbers generated: 2,000,000
//       Sum of random numbers: 999,628.37
//       Random number mean: 0.4998
//
//       Thread Main finished execution.
//       Random numbers generated: 2,000,000
//       Sum of random numbers: 999,920.89
//       Random number mean: 0.5000
//
//       Thread 3 finished execution.
//       Random numbers generated: 2,000,000
//       Sum of random numbers: 999,370.45
//       Random number mean: 0.4997
//
//       Thread 7 finished execution.
//       Random numbers generated: 2,000,000
//       Sum of random numbers: 999,330.92
//       Random number mean: 0.4997
//
//       Thread 9 finished execution.
//       Random numbers generated: 2,000,000
//       Sum of random numbers: 1,000,172.79
//       Random number mean: 0.5001
//
//       Thread 5 finished execution.
//       Random numbers generated: 2,000,000
//       Sum of random numbers: 1,000,079.43
//       Random number mean: 0.5000
//
//       Thread 1 finished execution.
//       Random numbers generated: 2,000,000
//       Sum of random numbers: 999,817.91
//       Random number mean: 0.4999
//
//       Thread 2 finished execution.
//       Random numbers generated: 2,000,000
//       Sum of random numbers: 999,930.63
//       Random number mean: 0.5000
//
//
//       Total random numbers generated: 22,000,000
//       Total sum of all random numbers: 10,998,238.98
//       Random number mean: 0.4999
open System
open System.Threading

type Example() =
    [<ThreadStatic; DefaultValue>]
    static val mutable private previous : float
    
    [<ThreadStatic; DefaultValue>]
    static val mutable private perThreadCtr : int

    [<ThreadStatic; DefaultValue>]
    static val mutable private perThreadTotal : float

    static let source = new CancellationTokenSource()
    static let countdown = new CountdownEvent(1)
    static let randLock = obj ()
    static let numericLock = obj ()
    static let rand = Random()

    let mutable totalValue = 0.0
    let mutable totalCount = 0

    member _.GetRandomNumbers(token: CancellationToken) =
        let mutable result = 0.0
        countdown.AddCount 1
        try 
            try
                for _ = 0 to 1999999 do
                    // Make sure there's no corruption of Random.
                    token.ThrowIfCancellationRequested()

                    lock randLock (fun () -> 
                        result <- rand.NextDouble() )

                    // Check for corruption of Random instance.
                    if result = Example.previous && result = 0.0 then 
                        source.Cancel()
                    else
                        Example.previous <- result
                        
                    Example.perThreadCtr <- Example.perThreadCtr + 1
                    Example.perThreadTotal <- Example.perThreadTotal + result

                // Update overall totals.
                lock numericLock (fun () ->
                    // Show result.
                    printfn "Thread %s finished execution." Thread.CurrentThread.Name
                    printfn $"Random numbers generated: {Example.perThreadCtr:N0}" 
                    printfn $"Sum of random numbers: {Example.perThreadTotal:N2}" 
                    printfn $"Random number mean: {(Example.perThreadTotal / float Example.perThreadCtr):N4}\n"
                    
                    // Update overall totals.
                    totalCount <- totalCount + Example.perThreadCtr
                    totalValue <- totalValue + Example.perThreadTotal)

            with :? OperationCanceledException as e -> 
                printfn "Corruption in Thread %s %s" (e.GetType().Name) Thread.CurrentThread.Name
        finally
            countdown.Signal() |> ignore

    member this.Execute() =
        let token = source.Token
        for i = 1 to 10 do 
            let newThread = Thread(fun () -> this.GetRandomNumbers token)
            newThread.Name <- string i
            newThread.Start()
        this.GetRandomNumbers token
        
        countdown.Signal() |> ignore

        countdown.Wait()

        source.Dispose()

        printfn $"\nTotal random numbers generated: {totalCount:N0}"
        printfn $"Total sum of all random numbers: {totalValue:N2}"
        printfn $"Random number mean: {(totalValue / float totalCount):N4}"

let ex = Example()
Thread.CurrentThread.Name <- "Main"
ex.Execute()

// The example displays output like the following:
//       Thread 6 finished execution.
//       Random numbers generated: 2,000,000
//       Sum of random numbers: 1,000,491.05
//       Random number mean: 0.5002
//
//       Thread 10 finished execution.
//       Random numbers generated: 2,000,000
//       Sum of random numbers: 999,329.64
//       Random number mean: 0.4997
//
//       Thread 4 finished execution.
//       Random numbers generated: 2,000,000
//       Sum of random numbers: 1,000,166.89
//       Random number mean: 0.5001
//
//       Thread 8 finished execution.
//       Random numbers generated: 2,000,000
//       Sum of random numbers: 999,628.37
//       Random number mean: 0.4998
//
//       Thread Main finished execution.
//       Random numbers generated: 2,000,000
//       Sum of random numbers: 999,920.89
//       Random number mean: 0.5000
//
//       Thread 3 finished execution.
//       Random numbers generated: 2,000,000
//       Sum of random numbers: 999,370.45
//       Random number mean: 0.4997
//
//       Thread 7 finished execution.
//       Random numbers generated: 2,000,000
//       Sum of random numbers: 999,330.92
//       Random number mean: 0.4997
//
//       Thread 9 finished execution.
//       Random numbers generated: 2,000,000
//       Sum of random numbers: 1,000,172.79
//       Random number mean: 0.5001
//
//       Thread 5 finished execution.
//       Random numbers generated: 2,000,000
//       Sum of random numbers: 1,000,079.43
//       Random number mean: 0.5000
//
//       Thread 1 finished execution.
//       Random numbers generated: 2,000,000
//       Sum of random numbers: 999,817.91
//       Random number mean: 0.4999
//
//       Thread 2 finished execution.
//       Random numbers generated: 2,000,000
//       Sum of random numbers: 999,930.63
//       Random number mean: 0.5000
//
//
//       Total random numbers generated: 22,000,000
//       Total sum of all random numbers: 10,998,238.98
//       Random number mean: 0.4999
Imports System.Threading

Module Example15
    <ThreadStatic> Dim previous As Double = 0.0
    <ThreadStatic> Dim perThreadCtr As Integer = 0
    <ThreadStatic> Dim perThreadTotal As Double = 0.0
    Dim source As New CancellationTokenSource()
    Dim countdown As New CountdownEvent(1)
    Dim randLock As New Object()
    Dim numericLock As New Object()
    Dim rand As New Random()
    Dim totalValue As Double = 0.0
    Dim totalCount As Integer = 0

    Public Sub Main()
        Thread.CurrentThread.Name = "Main"

        Dim token As CancellationToken = source.Token
        For threads As Integer = 1 To 10
            Dim newThread As New Thread(AddressOf GetRandomNumbers)
            newThread.Name = threads.ToString()
            newThread.Start(token)
        Next
        GetRandomNumbers(token)

        countdown.Signal()
        ' Make sure all threads have finished.
        countdown.Wait()

        Console.WriteLine()
        Console.WriteLine("Total random numbers generated: {0:N0}", totalCount)
        Console.WriteLine("Total sum of all random numbers: {0:N2}", totalValue)
        Console.WriteLine("Random number mean: {0:N4}", totalValue / totalCount)
    End Sub

    Private Sub GetRandomNumbers(o As Object)
        Dim token As CancellationToken = CType(o, CancellationToken)
        Dim result As Double = 0.0
        countdown.AddCount(1)

        Try
            For ctr As Integer = 1 To 2000000
                ' Make sure there's no corruption of Random.
                token.ThrowIfCancellationRequested()

                SyncLock randLock
                    result = rand.NextDouble()
                End SyncLock
                ' Check for corruption of Random instance.
                If result = previous AndAlso result = 0 Then
                    source.Cancel()
                Else
                    previous = result
                End If
                perThreadCtr += 1
                perThreadTotal += result
            Next

            Console.WriteLine("Thread {0} finished execution.",
                           Thread.CurrentThread.Name)
            Console.WriteLine("Random numbers generated: {0:N0}", perThreadCtr)
            Console.WriteLine("Sum of random numbers: {0:N2}", perThreadTotal)
            Console.WriteLine("Random number mean: {0:N4}", perThreadTotal / perThreadCtr)
            Console.WriteLine()

            ' Update overall totals.
            SyncLock numericLock
                totalCount += perThreadCtr
                totalValue += perThreadTotal
            End SyncLock
        Catch e As OperationCanceledException
            Console.WriteLine("Corruption in Thread {1}", e.GetType().Name, Thread.CurrentThread.Name)
        Finally
            countdown.Signal()
            source.Dispose()
        End Try
    End Sub
End Module
' The example displays output like the following:
'       Thread 6 finished execution.
'       Random numbers generated: 2,000,000
'       Sum of random numbers: 1,000,491.05
'       Random number mean: 0.5002
'       
'       Thread 10 finished execution.
'       Random numbers generated: 2,000,000
'       Sum of random numbers: 999,329.64
'       Random number mean: 0.4997
'       
'       Thread 4 finished execution.
'       Random numbers generated: 2,000,000
'       Sum of random numbers: 1,000,166.89
'       Random number mean: 0.5001
'       
'       Thread 8 finished execution.
'       Random numbers generated: 2,000,000
'       Sum of random numbers: 999,628.37
'       Random number mean: 0.4998
'       
'       Thread Main finished execution.
'       Random numbers generated: 2,000,000
'       Sum of random numbers: 999,920.89
'       Random number mean: 0.5000
'       
'       Thread 3 finished execution.
'       Random numbers generated: 2,000,000
'       Sum of random numbers: 999,370.45
'       Random number mean: 0.4997
'       
'       Thread 7 finished execution.
'       Random numbers generated: 2,000,000
'       Sum of random numbers: 999,330.92
'       Random number mean: 0.4997
'       
'       Thread 9 finished execution.
'       Random numbers generated: 2,000,000
'       Sum of random numbers: 1,000,172.79
'       Random number mean: 0.5001
'       
'       Thread 5 finished execution.
'       Random numbers generated: 2,000,000
'       Sum of random numbers: 1,000,079.43
'       Random number mean: 0.5000
'       
'       Thread 1 finished execution.
'       Random numbers generated: 2,000,000
'       Sum of random numbers: 999,817.91
'       Random number mean: 0.4999
'       
'       Thread 2 finished execution.
'       Random numbers generated: 2,000,000
'       Sum of random numbers: 999,930.63
'       Random number mean: 0.5000
'       
'       
'       Total random numbers generated: 22,000,000
'       Total sum of all random numbers: 10,998,238.98
'       Random number mean: 0.4999

此範例會以下列方式確保線程安全性:

  • ThreadStaticAttribute屬性可用來定義線程局部變數,以追蹤產生的隨機數總數,以及每個線程的總和。
  • 鎖定( lock C# 中的 語句、 lock F# 中的函式和 SyncLock Visual Basic 中的 語句)可保護所有線程上產生之所有隨機數總數和總和之變數的存取權。
  • 旗號 ( CountdownEvent 物件) 是用來確保主線程封鎖,直到所有其他線程完成執行為止。
  • 此範例會藉由判斷隨機數產生方法的兩個連續呼叫是否傳回 0,來檢查隨機數產生器是否已損毀。 如果偵測到損毀,此範例會使用 CancellationTokenSource 物件來表示應該取消所有線程。
  • 產生每個隨機數之前,每個線程都會檢查物件的狀態 CancellationToken 。 如果要求取消,此範例會呼叫 CancellationToken.ThrowIfCancellationRequested 方法來取消線程。

下列範例與第一個 Task 範例相同,不同之處在於它會使用 物件和 Lambda 表達式, Thread 而不是物件。

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

public class Example15
{
    static Object randLock, numericLock;
    static Random rand;
    static CancellationTokenSource source;
    double totalValue = 0.0;
    int totalCount = 0;

    public Example15()
    {
        rand = new Random();
        randLock = new Object();
        numericLock = new Object();
        source = new CancellationTokenSource();
    }

    public static async Task Main()
    {
        Example15 ex = new Example15();
        Thread.CurrentThread.Name = "Main";
        await ex.Execute();
    }

    private async Task Execute()
    {
        List<Task> tasks = new List<Task>();

        for (int ctr = 0; ctr <= 10; ctr++)
        {
            CancellationToken token = source.Token;
            int taskNo = ctr;
            tasks.Add(Task.Run(() =>
               {
                   double previous = 0.0;
                   int taskCtr = 0;
                   double taskTotal = 0.0;
                   double result = 0.0;

                   for (int n = 0; n < 2000000; n++)
                   {
                       // Make sure there's no corruption of Random.
                       token.ThrowIfCancellationRequested();

                       lock (randLock)
                       {
                           result = rand.NextDouble();
                       }
                       // Check for corruption of Random instance.
                       if ((result == previous) && result == 0)
                       {
                           source.Cancel();
                       }
                       else
                       {
                           previous = result;
                       }
                       taskCtr++;
                       taskTotal += result;
                   }

                   // Show result.
                   Console.WriteLine("Task {0} finished execution.", taskNo);
                   Console.WriteLine("Random numbers generated: {0:N0}", taskCtr);
                   Console.WriteLine("Sum of random numbers: {0:N2}", taskTotal);
                   Console.WriteLine("Random number mean: {0:N4}\n", taskTotal / taskCtr);

                   // Update overall totals.
                   lock (numericLock)
                   {
                       totalCount += taskCtr;
                       totalValue += taskTotal;
                   }
               },
            token));
        }
        try
        {
            await Task.WhenAll(tasks.ToArray());
            Console.WriteLine("\nTotal random numbers generated: {0:N0}", totalCount);
            Console.WriteLine("Total sum of all random numbers: {0:N2}", totalValue);
            Console.WriteLine("Random number mean: {0:N4}", totalValue / totalCount);
        }
        catch (AggregateException e)
        {
            foreach (Exception inner in e.InnerExceptions)
            {
                TaskCanceledException canc = inner as TaskCanceledException;
                if (canc != null)
                    Console.WriteLine("Task #{0} cancelled.", canc.Task.Id);
                else
                    Console.WriteLine("Exception: {0}", inner.GetType().Name);
            }
        }
        finally
        {
            source.Dispose();
        }
    }
}
// The example displays output like the following:
//       Task 1 finished execution.
//       Random numbers generated: 2,000,000
//       Sum of random numbers: 1,000,502.47
//       Random number mean: 0.5003
//
//       Task 0 finished execution.
//       Random numbers generated: 2,000,000
//       Sum of random numbers: 1,000,445.63
//       Random number mean: 0.5002
//
//       Task 2 finished execution.
//       Random numbers generated: 2,000,000
//       Sum of random numbers: 1,000,556.04
//       Random number mean: 0.5003
//
//       Task 3 finished execution.
//       Random numbers generated: 2,000,000
//       Sum of random numbers: 1,000,178.87
//       Random number mean: 0.5001
//
//       Task 4 finished execution.
//       Random numbers generated: 2,000,000
//       Sum of random numbers: 999,819.17
//       Random number mean: 0.4999
//
//       Task 5 finished execution.
//       Random numbers generated: 2,000,000
//       Sum of random numbers: 1,000,190.58
//       Random number mean: 0.5001
//
//       Task 6 finished execution.
//       Random numbers generated: 2,000,000
//       Sum of random numbers: 999,720.21
//       Random number mean: 0.4999
//
//       Task 7 finished execution.
//       Random numbers generated: 2,000,000
//       Sum of random numbers: 999,000.96
//       Random number mean: 0.4995
//
//       Task 8 finished execution.
//       Random numbers generated: 2,000,000
//       Sum of random numbers: 999,499.33
//       Random number mean: 0.4997
//
//       Task 9 finished execution.
//       Random numbers generated: 2,000,000
//       Sum of random numbers: 1,000,193.25
//       Random number mean: 0.5001
//
//       Task 10 finished execution.
//       Random numbers generated: 2,000,000
//       Sum of random numbers: 999,960.82
//       Random number mean: 0.5000
//
//
//       Total random numbers generated: 22,000,000
//       Total sum of all random numbers: 11,000,067.33
//       Random number mean: 0.5000
open System
open System.Threading
open System.Threading.Tasks

type Example() =
    static let source = new CancellationTokenSource()
    static let rand = Random()

    static let randLock = obj ()
    static let numericLock = obj ()

    let mutable totalValue = 0.0
    let mutable totalCount = 0

    member _.Execute() =
        use source = source // Dispose of the CancellationTokenSource when we're done with it.
        let token = source.Token

        let tasks =
            [| for i = 0 to 10 do
                   Task.Run(
                       (fun () ->
                           let mutable previous = 0.0
                           let mutable taskCtr = 0
                           let mutable taskTotal = 0.0
                           let mutable result = 0.0

                           for _ = 1 to 2000000 do
                               // Make sure there's no corruption of Random.
                               token.ThrowIfCancellationRequested()

                               lock randLock (fun () -> result <- rand.NextDouble())

                               // Check for corruption of Random instance.
                               if result = previous && result = 0.0 then
                                   source.Cancel()
                               else
                                   previous <- result

                               taskCtr <- taskCtr + 1
                               taskTotal <- taskTotal + result

                           lock numericLock (fun () ->
                               // Show result.
                               printfn "Task %i finished execution." i
                               printfn $"Random numbers generated: {taskCtr:N0}"
                               printfn $"Sum of random numbers: {taskTotal:N2}"
                               printfn $"Random number mean: {(taskTotal / float taskCtr):N4}\n"

                               // Update overall totals.
                               totalCount <- totalCount + taskCtr
                               totalValue <- totalValue + taskTotal)),
                       token
                   ) |]

        try
            // Run tasks with F# Async.
            Task.WhenAll tasks
            |> Async.AwaitTask
            |> Async.RunSynchronously

            printfn $"\nTotal random numbers generated: {totalCount:N0}"
            printfn $"Total sum of all random numbers: {totalValue:N2}"
            printfn $"Random number mean: {(totalValue / float totalCount):N4}"
        with
        | :? AggregateException as e ->
            for inner in e.InnerExceptions do
                match inner with
                | :? TaskCanceledException as canc ->
                    if canc <> null then
                        printfn $"Task #{canc.Task.Id} cancelled"
                    else
                        printfn $"Exception: {inner.GetType().Name}"
                | _ -> ()

let ex = Example()
Thread.CurrentThread.Name <- "Main"
ex.Execute()

// The example displays output like the following:
//       Task 1 finished execution.
//       Random numbers generated: 2,000,000
//       Sum of random numbers: 1,000,502.47
//       Random number mean: 0.5003
//
//       Task 0 finished execution.
//       Random numbers generated: 2,000,000
//       Sum of random numbers: 1,000,445.63
//       Random number mean: 0.5002
//
//       Task 2 finished execution.
//       Random numbers generated: 2,000,000
//       Sum of random numbers: 1,000,556.04
//       Random number mean: 0.5003
//
//       Task 3 finished execution.
//       Random numbers generated: 2,000,000
//       Sum of random numbers: 1,000,178.87
//       Random number mean: 0.5001
//
//       Task 4 finished execution.
//       Random numbers generated: 2,000,000
//       Sum of random numbers: 999,819.17
//       Random number mean: 0.4999
//
//       Task 5 finished execution.
//       Random numbers generated: 2,000,000
//       Sum of random numbers: 1,000,190.58
//       Random number mean: 0.5001
//
//       Task 6 finished execution.
//       Random numbers generated: 2,000,000
//       Sum of random numbers: 999,720.21
//       Random number mean: 0.4999
//
//       Task 7 finished execution.
//       Random numbers generated: 2,000,000
//       Sum of random numbers: 999,000.96
//       Random number mean: 0.4995
//
//       Task 8 finished execution.
//       Random numbers generated: 2,000,000
//       Sum of random numbers: 999,499.33
//       Random number mean: 0.4997
//
//       Task 9 finished execution.
//       Random numbers generated: 2,000,000
//       Sum of random numbers: 1,000,193.25
//       Random number mean: 0.5001
//
//       Task 10 finished execution.
//       Random numbers generated: 2,000,000
//       Sum of random numbers: 999,960.82
//       Random number mean: 0.5000
//
//
//       Total random numbers generated: 22,000,000
//       Total sum of all random numbers: 11,000,067.33
//       Random number mean: 0.5000
Imports System.Collections.Generic
Imports System.Threading
Imports System.Threading.Tasks

Module Example16
    Dim source As New CancellationTokenSource()
    Dim randLock As New Object()
    Dim numericLock As New Object()
    Dim rand As New Random()
    Dim totalValue As Double = 0.0
    Dim totalCount As Integer = 0

    Public Sub Main()
        Dim tasks As New List(Of Task)()

        For ctr As Integer = 1 To 10
            Dim token As CancellationToken = source.Token
            Dim taskNo As Integer = ctr
            tasks.Add(Task.Run(
                   Sub()
                       Dim previous As Double = 0.0
                       Dim taskCtr As Integer = 0
                       Dim taskTotal As Double = 0.0
                       Dim result As Double = 0.0

                       For n As Integer = 1 To 2000000
                           ' Make sure there's no corruption of Random.
                           token.ThrowIfCancellationRequested()

                           SyncLock randLock
                               result = rand.NextDouble()
                           End SyncLock
                           ' Check for corruption of Random instance.
                           If result = previous AndAlso result = 0 Then
                               source.Cancel()
                           Else
                               previous = result
                           End If
                           taskCtr += 1
                           taskTotal += result
                       Next

                       ' Show result.
                       Console.WriteLine("Task {0} finished execution.", taskNo)
                       Console.WriteLine("Random numbers generated: {0:N0}", taskCtr)
                       Console.WriteLine("Sum of random numbers: {0:N2}", taskTotal)
                       Console.WriteLine("Random number mean: {0:N4}", taskTotal / taskCtr)
                       Console.WriteLine()

                       ' Update overall totals.
                       SyncLock numericLock
                           totalCount += taskCtr
                           totalValue += taskTotal
                       End SyncLock
                   End Sub, token))
        Next

        Try
            Task.WaitAll(tasks.ToArray())
            Console.WriteLine()
            Console.WriteLine("Total random numbers generated: {0:N0}", totalCount)
            Console.WriteLine("Total sum of all random numbers: {0:N2}", totalValue)
            Console.WriteLine("Random number mean: {0:N4}", totalValue / totalCount)
        Catch e As AggregateException
            For Each inner As Exception In e.InnerExceptions
                Dim canc As TaskCanceledException = TryCast(inner, TaskCanceledException)
                If canc IsNot Nothing Then
                    Console.WriteLine("Task #{0} cancelled.", canc.Task.Id)
                Else
                    Console.WriteLine("Exception: {0}", inner.GetType().Name)
                End If
            Next
        Finally
            source.Dispose()
        End Try
    End Sub
End Module
' The example displays output like the following:
'       Task 1 finished execution.
'       Random numbers generated: 2,000,000
'       Sum of random numbers: 1,000,502.47
'       Random number mean: 0.5003
'       
'       Task 0 finished execution.
'       Random numbers generated: 2,000,000
'       Sum of random numbers: 1,000,445.63
'       Random number mean: 0.5002
'       
'       Task 2 finished execution.
'       Random numbers generated: 2,000,000
'       Sum of random numbers: 1,000,556.04
'       Random number mean: 0.5003
'       
'       Task 3 finished execution.
'       Random numbers generated: 2,000,000
'       Sum of random numbers: 1,000,178.87
'       Random number mean: 0.5001
'       
'       Task 4 finished execution.
'       Random numbers generated: 2,000,000
'       Sum of random numbers: 999,819.17
'       Random number mean: 0.4999
'       
'       Task 5 finished execution.
'       Random numbers generated: 2,000,000
'       Sum of random numbers: 1,000,190.58
'       Random number mean: 0.5001
'       
'       Task 6 finished execution.
'       Random numbers generated: 2,000,000
'       Sum of random numbers: 999,720.21
'       Random number mean: 0.4999
'       
'       Task 7 finished execution.
'       Random numbers generated: 2,000,000
'       Sum of random numbers: 999,000.96
'       Random number mean: 0.4995
'       
'       Task 8 finished execution.
'       Random numbers generated: 2,000,000
'       Sum of random numbers: 999,499.33
'       Random number mean: 0.4997
'       
'       Task 9 finished execution.
'       Random numbers generated: 2,000,000
'       Sum of random numbers: 1,000,193.25
'       Random number mean: 0.5001
'       
'       Task 10 finished execution.
'       Random numbers generated: 2,000,000
'       Sum of random numbers: 999,960.82
'       Random number mean: 0.5000
'       
'       
'       Total random numbers generated: 22,000,000
'       Total sum of all random numbers: 11,000,067.33
'       Random number mean: 0.5000

其與第一個範例不同,方式如下:

  • 要追蹤所產生隨機數數目的變數,而且每個工作中的總和都是工作本機的,因此不需要使用 ThreadStaticAttribute 屬性。
  • 靜態 Task.WaitAll 方法可用來確保所有工作都完成之前,主線程不會完成。 不需要 CountdownEvent 物件。
  • 工作取消所產生的例外狀況會顯示在方法中 Task.WaitAll 。 在上一個範例中,它會由每個線程處理。

產生不同類型的隨機數

隨機數產生器提供方法,可讓您產生下列類型的隨機數:

  • 一系列 Byte 值。 您可以藉由將初始化的陣列傳遞至您希望方法傳回 NextBytes 方法的元素數目,以判斷位元組值的數目。 下列範例會產生 20 個字節。

    Random rnd = new Random();
    Byte[] bytes = new Byte[20];
    rnd.NextBytes(bytes);
    for (int ctr = 1; ctr <= bytes.Length; ctr++)
    {
        Console.Write("{0,3}   ", bytes[ctr - 1]);
        if (ctr % 10 == 0) Console.WriteLine();
    }
    
    // The example displays output like the following:
    //       141    48   189    66   134   212   211    71   161    56
    //       181   166   220   133     9   252   222    57    62    62
    
    let rnd = Random()
    let bytes = Array.zeroCreate 20
    rnd.NextBytes bytes
    
    for i = 1 to bytes.Length do
        printf "%3i   " bytes.[i - 1]
        if (i % 10 = 0) then printfn ""
    
    // The example displays output like the following:
    //       141    48   189    66   134   212   211    71   161    56
    //       181   166   220   133     9   252   222    57    62    62
    
    Module Example9
        Public Sub Main()
            Dim rnd As New Random()
            Dim bytes(19) As Byte
            rnd.NextBytes(bytes)
            For ctr As Integer = 1 To bytes.Length
                Console.Write("{0,3}   ", bytes(ctr - 1))
                If ctr Mod 10 = 0 Then Console.WriteLine()
            Next
        End Sub
    End Module
    ' The example displays output like the following:
    '       141    48   189    66   134   212   211    71   161    56
    '       181   166   220   133     9   252   222    57    62    62
    
  • 單一整數。 您可以藉由呼叫 Next() 方法、呼叫 方法介於 0 與特定值Next(Int32)之間的整數,或呼叫 方法,或呼叫 Next(Int32, Int32) 方法,在值範圍內的整數,選擇從 0 到最大值 (Int32.MaxValue- 1) 的整數。 在參數化多載中,指定的最大值是獨佔的;也就是說,產生的實際最大數目小於指定的值。

    下列範例會呼叫 Next(Int32, Int32) 方法,以產生 -10 到 10 之間的 10 個隨機數。 請注意,方法的第二個自變數會指定方法所傳回之隨機值範圍的獨佔上限。 換句話說,方法可傳回的最大整數是小於這個值。

    Random rnd = new Random();
    for (int ctr = 0; ctr < 10; ctr++)
    {
        Console.Write("{0,3}   ", rnd.Next(-10, 11));
    }
    
    // The example displays output like the following:
    //    2     9    -3     2     4    -7    -3    -8    -8     5
    
    let rnd = Random()
    for i = 0 to 9 do 
        printf "%3i   " (rnd.Next(-10, 11))
    
    // The example displays output like the following:
    //    2     9    -3     2     4    -7    -3    -8    -8     5
    
    Module Example11
        Public Sub Main()
            Dim rnd As New Random()
            For ctr As Integer = 0 To 9
                Console.Write("{0,3}   ", rnd.Next(-10, 11))
            Next
        End Sub
    End Module
    ' The example displays output like the following:
    '    2     9    -3     2     4    -7    -3    -8    -8     5
    
  • 透過呼叫 NextDouble 方法,將單一浮點值從0.0到小於1.0。 方法傳回之隨機數的獨佔上限為 1,因此其實際上限為 0.9999999999999999978。 下列範例會產生10個隨機浮點數。

    Random rnd = new Random();
    for (int ctr = 0; ctr < 10; ctr++)
    {
        Console.Write("{0,-19:R}   ", rnd.NextDouble());
        if ((ctr + 1) % 3 == 0) Console.WriteLine();
    }
    
    // The example displays output like the following:
    //    0.7911680553998649    0.0903414949264105    0.79776258291572455
    //    0.615568345233597     0.652644504165577     0.84023809378977776
    //    0.099662564741290441   0.91341467383942321  0.96018602045261581
    //    0.74772306473354022
    
    let rnd = Random()
    for i = 0 to 9 do 
        printf $"{rnd.NextDouble(),-19:R}   "
        if (i + 1) % 3 = 0 then printfn ""
    
    // The example displays output like the following:
    //    0.7911680553998649    0.0903414949264105    0.79776258291572455
    //    0.615568345233597     0.652644504165577     0.84023809378977776
    //    0.099662564741290441   0.91341467383942321  0.96018602045261581
    //    0.74772306473354022
    
    Module Example10
        Public Sub Main()
            Dim rnd As New Random()
            For ctr As Integer = 0 To 9
                Console.Write("{0,-19:R}   ", rnd.NextDouble())
                If (ctr + 1) Mod 3 = 0 Then Console.WriteLine()
            Next
        End Sub
    End Module
    ' The example displays output like the following:
    '    0.7911680553998649    0.0903414949264105    0.79776258291572455    
    '    0.615568345233597     0.652644504165577     0.84023809378977776   
    '    0.099662564741290441  0.91341467383942321   0.96018602045261581   
    '    0.74772306473354022
    

重要

Next(Int32, Int32)方法可讓您指定傳回隨機數的範圍。 不過, maxValue 指定傳回數位上限範圍的參數是獨佔值,而非內含值。 這表示方法呼叫 Next(0, 100) 會傳回介於 0 到 99 之間的值,而不是介於 0 到 100 之間。

您也可以針對這類工作使用 Random 類別,例如產生隨機布爾值、在指定範圍中產生隨機浮點值、產生隨機 64 位整數,以及從數位或集合擷取唯一元素。

替代您自己的演算法

您可以繼承自 Random 類別並提供隨機數產生演算法,以實作您自己的隨機數產生器。 若要提供您自己的演算法,您必須覆寫 Sample 方法,此方法會實作隨機數產生演算法。 您也應該覆寫 Next()Next(Int32, Int32)NextBytes 方法,以確保它們會呼叫覆寫 Sample 的方法。 您不需要覆寫 Next(Int32)NextDouble 方法。

如需衍生自 Random 類別並修改其預設虛擬隨機數產生器的範例,請參閱 Sample 參考頁面。

擷取相同的隨機值序列

有時候您想要在軟體測試案例和遊戲中產生相同的隨機數序列。 使用相同隨機數序列進行測試,可讓您偵測回歸並確認錯誤修正。 在遊戲中使用相同的隨機數序列,可讓您重新執行先前的遊戲。

您可以將相同的種子值 Random(Int32) 提供給建構函式,以產生相同的隨機數序列。 種子值會提供虛擬隨機數產生演算法的起始值。 下列範例使用 100100 做為任意種子值來具現化 Random 對象、顯示 20 個隨機浮點值,並保存種子值。 然後,它會還原種子值、具現化新的隨機數產生器,並顯示相同的 20 個隨機浮點值。 請注意,如果在不同的 .NET 版本上執行,此範例可能會產生不同的隨機數序列。

using System;
using System.IO;

public class Example12
{
    public static void Main()
    {
        int seed = 100100;
        ShowRandomNumbers(seed);
        Console.WriteLine();

        PersistSeed(seed);

        DisplayNewRandomNumbers();
    }

    private static void ShowRandomNumbers(int seed)
    {
        Random rnd = new Random(seed);
        for (int ctr = 0; ctr <= 20; ctr++)
            Console.WriteLine(rnd.NextDouble());
    }

    private static void PersistSeed(int seed)
    {
        FileStream fs = new FileStream(@".\seed.dat", FileMode.Create);
        BinaryWriter bin = new BinaryWriter(fs);
        bin.Write(seed);
        bin.Close();
    }

    private static void DisplayNewRandomNumbers()
    {
        FileStream fs = new FileStream(@".\seed.dat", FileMode.Open);
        BinaryReader bin = new BinaryReader(fs);
        int seed = bin.ReadInt32();
        bin.Close();

        Random rnd = new Random(seed);
        for (int ctr = 0; ctr <= 20; ctr++)
            Console.WriteLine(rnd.NextDouble());
    }
}
// The example displays output like the following:
//       0.500193602172748
//       0.0209461245783354
//       0.465869495396442
//       0.195512794514891
//       0.928583675496552
//       0.729333720509584
//       0.381455668891527
//       0.0508996467343064
//       0.019261200921266
//       0.258578445417145
//       0.0177532266908107
//       0.983277184415272
//       0.483650274334313
//       0.0219647376900375
//       0.165910115077118
//       0.572085966622497
//       0.805291457942357
//       0.927985211335116
//       0.4228545699375
//       0.523320379910674
//       0.157783938645285
//
//       0.500193602172748
//       0.0209461245783354
//       0.465869495396442
//       0.195512794514891
//       0.928583675496552
//       0.729333720509584
//       0.381455668891527
//       0.0508996467343064
//       0.019261200921266
//       0.258578445417145
//       0.0177532266908107
//       0.983277184415272
//       0.483650274334313
//       0.0219647376900375
//       0.165910115077118
//       0.572085966622497
//       0.805291457942357
//       0.927985211335116
//       0.4228545699375
//       0.523320379910674
//       0.157783938645285
open System
open System.IO

let showRandomNumbers seed =
    let rnd = Random seed
    for _ = 0 to 20 do 
        printfn $"{rnd.NextDouble()}"

let persistSeed (seed: int) =
    use bin = new BinaryWriter(new FileStream(@".\seed.dat", FileMode.Create))
    bin.Write seed

let displayNewRandomNumbers () =
    use bin = new BinaryReader(new FileStream(@".\seed.dat", FileMode.Open))
    let seed = bin.ReadInt32()

    let rnd = Random seed
    for _ = 0 to 20 do 
        printfn $"{rnd.NextDouble()}"

let seed = 100100
showRandomNumbers seed
printfn ""

persistSeed seed

displayNewRandomNumbers ()

// The example displays output like the following:
//       0.500193602172748
//       0.0209461245783354
//       0.465869495396442
//       0.195512794514891
//       0.928583675496552
//       0.729333720509584
//       0.381455668891527
//       0.0508996467343064
//       0.019261200921266
//       0.258578445417145
//       0.0177532266908107
//       0.983277184415272
//       0.483650274334313
//       0.0219647376900375
//       0.165910115077118
//       0.572085966622497
//       0.805291457942357
//       0.927985211335116
//       0.4228545699375
//       0.523320379910674
//       0.157783938645285
//
//       0.500193602172748
//       0.0209461245783354
//       0.465869495396442
//       0.195512794514891
//       0.928583675496552
//       0.729333720509584
//       0.381455668891527
//       0.0508996467343064
//       0.019261200921266
//       0.258578445417145
//       0.0177532266908107
//       0.983277184415272
//       0.483650274334313
//       0.0219647376900375
//       0.165910115077118
//       0.572085966622497
//       0.805291457942357
//       0.927985211335116
//       0.4228545699375
//       0.523320379910674
//       0.157783938645285
Imports System.IO

Module Example14
    Public Sub Main()
        Dim seed As Integer = 100100
        ShowRandomNumbers(seed)
        Console.WriteLine()

        PersistSeed(seed)

        DisplayNewRandomNumbers()
    End Sub

    Private Sub ShowRandomNumbers(seed As Integer)
        Dim rnd As New Random(seed)
        For ctr As Integer = 0 To 20
            Console.WriteLine(rnd.NextDouble())
        Next
    End Sub

    Private Sub PersistSeed(seed As Integer)
        Dim fs As New FileStream(".\seed.dat", FileMode.Create)
        Dim bin As New BinaryWriter(fs)
        bin.Write(seed)
        bin.Close()
    End Sub

    Private Sub DisplayNewRandomNumbers()
        Dim fs As New FileStream(".\seed.dat", FileMode.Open)
        Dim bin As New BinaryReader(fs)
        Dim seed As Integer = bin.ReadInt32()
        bin.Close()

        Dim rnd As New Random(seed)
        For ctr As Integer = 0 To 20
            Console.WriteLine(rnd.NextDouble())
        Next
    End Sub
End Module
' The example displays output like the following:
'       0.500193602172748
'       0.0209461245783354
'       0.465869495396442
'       0.195512794514891
'       0.928583675496552
'       0.729333720509584
'       0.381455668891527
'       0.0508996467343064
'       0.019261200921266
'       0.258578445417145
'       0.0177532266908107
'       0.983277184415272
'       0.483650274334313
'       0.0219647376900375
'       0.165910115077118
'       0.572085966622497
'       0.805291457942357
'       0.927985211335116
'       0.4228545699375
'       0.523320379910674
'       0.157783938645285
'       
'       0.500193602172748
'       0.0209461245783354
'       0.465869495396442
'       0.195512794514891
'       0.928583675496552
'       0.729333720509584
'       0.381455668891527
'       0.0508996467343064
'       0.019261200921266
'       0.258578445417145
'       0.0177532266908107
'       0.983277184415272
'       0.483650274334313
'       0.0219647376900375
'       0.165910115077118
'       0.572085966622497
'       0.805291457942357
'       0.927985211335116
'       0.4228545699375
'       0.523320379910674
'       0.157783938645285

擷取隨機數的唯一序列

將不同的種子值提供給 類別的 Random 實例,會導致每個隨機數產生器產生不同的值序列。 您可以藉由呼叫 Random(Int32) 建構函式,或藉由呼叫 Random() 建構函式來隱含地提供種子值。 大部分的開發人員都會呼叫使用系統時鐘的無參數建構函式。 下列範例會使用此方法來具現化兩 Random 個實例。 每個實例都會顯示一系列10個隨機整數。

using System;
using System.Threading;

public class Example16
{
    public static void Main()
    {
        Console.WriteLine("Instantiating two random number generators...");
        Random rnd1 = new Random();
        Thread.Sleep(2000);
        Random rnd2 = new Random();

        Console.WriteLine("\nThe first random number generator:");
        for (int ctr = 1; ctr <= 10; ctr++)
            Console.WriteLine("   {0}", rnd1.Next());

        Console.WriteLine("\nThe second random number generator:");
        for (int ctr = 1; ctr <= 10; ctr++)
            Console.WriteLine("   {0}", rnd2.Next());
    }
}
// The example displays output like the following:
//       Instantiating two random number generators...
//
//       The first random number generator:
//          643164361
//          1606571630
//          1725607587
//          2138048432
//          496874898
//          1969147632
//          2034533749
//          1840964542
//          412380298
//          47518930
//
//       The second random number generator:
//          1251659083
//          1514185439
//          1465798544
//          517841554
//          1821920222
//          195154223
//          1538948391
//          1548375095
//          546062716
//          897797880
open System
open System.Threading

printfn "Instantiating two random number generators..."
let rnd1 = Random()
Thread.Sleep 2000
let rnd2 = Random()

printfn "\nThe first random number generator:"
for _ = 1 to 10 do 
    printfn $"   {rnd1.Next()}"

printfn "\nThe second random number generator:"
for _ = 1 to 10 do 
    printfn $"   {rnd2.Next()}"

// The example displays output like the following:
//       Instantiating two random number generators...
//
//       The first random number generator:
//          643164361
//          1606571630
//          1725607587
//          2138048432
//          496874898
//          1969147632
//          2034533749
//          1840964542
//          412380298
//          47518930
//
//       The second random number generator:
//          1251659083
//          1514185439
//          1465798544
//          517841554
//          1821920222
//          195154223
//          1538948391
//          1548375095
//          546062716
//          897797880
Imports System.Threading

Module Example17
    Public Sub Main()
        Console.WriteLine("Instantiating two random number generators...")
        Dim rnd1 As New Random()
        Thread.Sleep(2000)
        Dim rnd2 As New Random()
        Console.WriteLine()

        Console.WriteLine("The first random number generator:")
        For ctr As Integer = 1 To 10
            Console.WriteLine("   {0}", rnd1.Next())
        Next
        Console.WriteLine()

        Console.WriteLine("The second random number generator:")
        For ctr As Integer = 1 To 10
            Console.WriteLine("   {0}", rnd2.Next())
        Next
    End Sub
End Module
' The example displays output like the following:
'       Instantiating two random number generators...
'       
'       The first random number generator:
'          643164361
'          1606571630
'          1725607587
'          2138048432
'          496874898
'          1969147632
'          2034533749
'          1840964542
'          412380298
'          47518930
'       
'       The second random number generator:
'          1251659083
'          1514185439
'          1465798544
'          517841554
'          1821920222
'          195154223
'          1538948391
'          1548375095
'          546062716
'          897797880

不過,由於其有限解析度,系統時鐘不會偵測不到大約 15 毫秒的時間差異。 因此,如果您的程式代碼在 .NET Framework 上呼叫 Random() 多載來連續具現化兩 Random 個物件,您可能會不小心為物件提供相同的種子值。 Random(.NET Core 中的 類別沒有這項限制。若要查看上一個範例中的這個,請批注化Thread.Sleep方法呼叫,然後再次編譯並執行此範例。

若要防止這種情況發生,建議您具現化單 Random 一物件,而不是多個物件。 不過,由於 Random 不是安全線程,因此如果您從多個線程存取 Random 實例,則必須使用某些同步處理裝置;如需詳細資訊,請參閱 線程安全性 一節。 或者,您可以使用延遲機制,例如 Sleep 上一個範例中使用的 方法,以確保具現化相距超過 15 毫秒。

擷取指定範圍內的整數

您可以藉由呼叫 Next(Int32, Int32) 方法來擷取指定範圍內的整數,這可讓您同時指定您想要隨機數產生器傳回之數位的下限和上限。 上限是獨佔值,而非內含值。 也就是說,它不包含在 方法所傳回的值範圍中。 下列範例會使用這個方法來產生介於 -10 到 10 之間的隨機整數。 請注意,它會指定 11,也就是大於所需值的 11,做為方法呼叫中自變數的值 maxValue

Random rnd = new Random();
for (int ctr = 1; ctr <= 15; ctr++)
{
    Console.Write("{0,3}    ", rnd.Next(-10, 11));
    if (ctr % 5 == 0) Console.WriteLine();
}

// The example displays output like the following:
//        -2     -5     -1     -2     10
//        -3      6     -4     -8      3
//        -7     10      5     -2      4
let rnd = Random()
for i = 1 to 15 do 
    printf "%3i    " (rnd.Next(-10, 11))
    if i % 5 = 0 then printfn ""
// The example displays output like the following:
//        -2     -5     -1     -2     10
//        -3      6     -4     -8      3
//        -7     10      5     -2      4
Module Example12
    Public Sub Main()
        Dim rnd As New Random()
        For ctr As Integer = 1 To 15
            Console.Write("{0,3}    ", rnd.Next(-10, 11))
            If ctr Mod 5 = 0 Then Console.WriteLine()
        Next
    End Sub
End Module
' The example displays output like the following:
'        -2     -5     -1     -2     10
'        -3      6     -4     -8      3
'        -7     10      5     -2      4

擷取具有指定位數的整數

您可以呼叫 Next(Int32, Int32) 方法來擷取具有指定數位數目的數位。 例如,若要擷取具有四個數字的數位(也就是範圍從 1000 到 9999 的數位),您可以呼叫 Next(Int32, Int32) 值為 1000 且maxValue值為 10000 的 方法minValue,如下列範例所示。

Random rnd = new Random();
for (int ctr = 1; ctr <= 50; ctr++)
{
    Console.Write("{0,3}    ", rnd.Next(1000, 10000));
    if (ctr % 10 == 0) Console.WriteLine();
}

// The example displays output like the following:
//    9570    8979    5770    1606    3818    4735    8495    7196    7070    2313
//    5279    6577    5104    5734    4227    3373    7376    6007    8193    5540
//    7558    3934    3819    7392    1113    7191    6947    4963    9179    7907
//    3391    6667    7269    1838    7317    1981    5154    7377    3297    5320
//    9869    8694    2684    4949    2999    3019    2357    5211    9604    2593
let rnd = Random()
for i = 1 to 50 do
    printf "%3i    " (rnd.Next(1000, 10000))
    if i % 10 = 0 then printfn ""

// The example displays output like the following:
//    9570    8979    5770    1606    3818    4735    8495    7196    7070    2313
//    5279    6577    5104    5734    4227    3373    7376    6007    8193    5540
//    7558    3934    3819    7392    1113    7191    6947    4963    9179    7907
//    3391    6667    7269    1838    7317    1981    5154    7377    3297    5320
//    9869    8694    2684    4949    2999    3019    2357    5211    9604    2593
Module Example13
    Public Sub Main()
        Dim rnd As New Random()
        For ctr As Integer = 1 To 50
            Console.Write("{0,3}    ", rnd.Next(1000, 10000))
            If ctr Mod 10 = 0 Then Console.WriteLine()
        Next
    End Sub
End Module
' The example displays output like the following:
'    9570    8979    5770    1606    3818    4735    8495    7196    7070    2313
'    5279    6577    5104    5734    4227    3373    7376    6007    8193    5540
'    7558    3934    3819    7392    1113    7191    6947    4963    9179    7907
'    3391    6667    7269    1838    7317    1981    5154    7377    3297    5320
'    9869    8694    2684    4949    2999    3019    2357    5211    9604    2593

擷取指定範圍中的浮點值

方法 NextDouble 會傳回範圍從 0 到小於 1 的隨機浮點值。 不過,您通常會想要在某些其他範圍中產生隨機值。

如果最小和最大所需值之間的間隔為 1,您可以將所需起始間隔與 0 之間的差異新增至 方法所 NextDouble 傳回的數位。 下列範例會執行此動作,以產生介於 -1 到 0 之間的 10 個隨機數。

Random rnd = new Random();
for (int ctr = 1; ctr <= 10; ctr++)
    Console.WriteLine(rnd.NextDouble() - 1);

// The example displays output like the following:
//       -0.930412760437658
//       -0.164699016215605
//       -0.9851692803135
//       -0.43468508843085
//       -0.177202483255976
//       -0.776813320245972
//       -0.0713201854710096
//       -0.0912875561468711
//       -0.540621722368813
//       -0.232211863730201
let rnd = Random()

for _ = 1 to 10 do
    printfn "%O" (rnd.NextDouble() - 1.0)

// The example displays output like the following:
//       -0.930412760437658
//       -0.164699016215605
//       -0.9851692803135
//       -0.43468508843085
//       -0.177202483255976
//       -0.776813320245972
//       -0.0713201854710096
//       -0.0912875561468711
//       -0.540621722368813
//       -0.232211863730201
Module Example6
    Public Sub Main()
        Dim rnd As New Random()
        For ctr As Integer = 1 To 10
            Console.WriteLine(rnd.NextDouble() - 1)
        Next
    End Sub
End Module
' The example displays output like the following:
'       -0.930412760437658
'       -0.164699016215605
'       -0.9851692803135
'       -0.43468508843085
'       -0.177202483255976
'       -0.776813320245972
'       -0.0713201854710096
'       -0.0912875561468711
'       -0.540621722368813
'       -0.232211863730201

若要產生下限為0但上限大於1的隨機浮點數(或者,如果負數,其下限小於 -1且上限為0),請將隨機數乘以非零界限。 下列範例會執行此動作,以產生 2000 萬個隨機浮點數,範圍從 0 到 Int64.MaxValue。 中也會顯示 方法所產生的隨機值分佈。

const long ONE_TENTH = 922337203685477581;

Random rnd = new Random();
double number;
int[] count = new int[10];

// Generate 20 million integer values between.
for (int ctr = 1; ctr <= 20000000; ctr++)
{
    number = rnd.NextDouble() * Int64.MaxValue;
    // Categorize random numbers into 10 groups.
    count[(int)(number / ONE_TENTH)]++;
}
// Display breakdown by range.
Console.WriteLine("{0,28} {1,32}   {2,7}\n", "Range", "Count", "Pct.");
for (int ctr = 0; ctr <= 9; ctr++)
    Console.WriteLine("{0,25:N0}-{1,25:N0}  {2,8:N0}   {3,7:P2}", ctr * ONE_TENTH,
                       ctr < 9 ? ctr * ONE_TENTH + ONE_TENTH - 1 : Int64.MaxValue,
                       count[ctr], count[ctr] / 20000000.0);

// The example displays output like the following:
//                           Range                            Count      Pct.
//
//                            0-  922,337,203,685,477,580  1,996,148    9.98 %
//      922,337,203,685,477,581-1,844,674,407,370,955,161  2,000,293   10.00 %
//    1,844,674,407,370,955,162-2,767,011,611,056,432,742  2,000,094   10.00 %
//    2,767,011,611,056,432,743-3,689,348,814,741,910,323  2,000,159   10.00 %
//    3,689,348,814,741,910,324-4,611,686,018,427,387,904  1,999,552   10.00 %
//    4,611,686,018,427,387,905-5,534,023,222,112,865,485  1,998,248    9.99 %
//    5,534,023,222,112,865,486-6,456,360,425,798,343,066  2,000,696   10.00 %
//    6,456,360,425,798,343,067-7,378,697,629,483,820,647  2,001,637   10.01 %
//    7,378,697,629,483,820,648-8,301,034,833,169,298,228  2,002,870   10.01 %
//    8,301,034,833,169,298,229-9,223,372,036,854,775,807  2,000,303   10.00 %
[<Literal>]
let ONE_TENTH = 922337203685477581L

let rnd = Random()

// Generate 20 million random integers.
let count =
    Array.init 20000000 (fun _ -> rnd.NextDouble() * (float Int64.MaxValue) )
    |> Array.countBy (fun x -> x / (float ONE_TENTH) |> int ) // Categorize into 10 groups and count them.
    |> Array.map snd

// Display breakdown by range.
printfn "%28s %32s   %7s\n" "Range" "Count" "Pct."
for i = 0 to 9 do
    let r1 = int64 i * ONE_TENTH
    let r2 = if i < 9 then r1 + ONE_TENTH - 1L else Int64.MaxValue
    printfn $"{r1,25:N0}-{r2,25:N0}  {count.[i],8:N0}   {float count.[i] / 20000000.0,7:P2}"

// The example displays output like the following:
//                           Range                            Count      Pct.
//
//                            0-  922,337,203,685,477,580  1,996,148    9.98 %
//      922,337,203,685,477,581-1,844,674,407,370,955,161  2,000,293   10.00 %
//    1,844,674,407,370,955,162-2,767,011,611,056,432,742  2,000,094   10.00 %
//    2,767,011,611,056,432,743-3,689,348,814,741,910,323  2,000,159   10.00 %
//    3,689,348,814,741,910,324-4,611,686,018,427,387,904  1,999,552   10.00 %
//    4,611,686,018,427,387,905-5,534,023,222,112,865,485  1,998,248    9.99 %
//    5,534,023,222,112,865,486-6,456,360,425,798,343,066  2,000,696   10.00 %
//    6,456,360,425,798,343,067-7,378,697,629,483,820,647  2,001,637   10.01 %
//    7,378,697,629,483,820,648-8,301,034,833,169,298,228  2,002,870   10.01 %
//    8,301,034,833,169,298,229-9,223,372,036,854,775,807  2,000,303   10.00 %
Module Example5
    Public Sub Main()
        Const ONE_TENTH As Long = 922337203685477581

        Dim rnd As New Random()
        Dim number As Long
        Dim count(9) As Integer

        ' Generate 20 million integer values.
        For ctr As Integer = 1 To 20000000
            number = CLng(rnd.NextDouble() * Int64.MaxValue)
            ' Categorize random numbers.
            count(CInt(number \ ONE_TENTH)) += 1
        Next
        ' Display breakdown by range.
        Console.WriteLine("{0,28} {1,32}   {2,7}", "Range", "Count", "Pct.")
        Console.WriteLine()
        For ctr As Integer = 0 To 9
            Console.WriteLine("{0,25:N0}-{1,25:N0}  {2,8:N0}   {3,7:P2}", ctr * ONE_TENTH,
                            If(ctr < 9, ctr * ONE_TENTH + ONE_TENTH - 1, Int64.MaxValue),
                            count(ctr), count(ctr) / 20000000)
        Next
    End Sub
End Module
' The example displays output like the following:
'                           Range                            Count      Pct.
'    
'                            0-  922,337,203,685,477,580  1,996,148    9.98 %
'      922,337,203,685,477,581-1,844,674,407,370,955,161  2,000,293   10.00 %
'    1,844,674,407,370,955,162-2,767,011,611,056,432,742  2,000,094   10.00 %
'    2,767,011,611,056,432,743-3,689,348,814,741,910,323  2,000,159   10.00 %
'    3,689,348,814,741,910,324-4,611,686,018,427,387,904  1,999,552   10.00 %
'    4,611,686,018,427,387,905-5,534,023,222,112,865,485  1,998,248    9.99 %
'    5,534,023,222,112,865,486-6,456,360,425,798,343,066  2,000,696   10.00 %
'    6,456,360,425,798,343,067-7,378,697,629,483,820,647  2,001,637   10.01 %
'    7,378,697,629,483,820,648-8,301,034,833,169,298,228  2,002,870   10.01 %
'    8,301,034,833,169,298,229-9,223,372,036,854,775,807  2,000,303   10.00 %

若要在兩個任意值之間產生隨機浮點數,就像 方法對整數所做的一樣 Next(Int32, Int32) ,請使用下列公式:

Random.NextDouble() * (maxValue - minValue) + minValue

下列範例會產生100萬個隨機數,範圍從10.0到11.0,並顯示其分佈。

Random rnd = new Random();
int lowerBound = 10;
int upperBound = 11;
int[] range = new int[10];
for (int ctr = 1; ctr <= 1000000; ctr++)
{
    Double value = rnd.NextDouble() * (upperBound - lowerBound) + lowerBound;
    range[(int)Math.Truncate((value - lowerBound) * 10)]++;
}

for (int ctr = 0; ctr <= 9; ctr++)
{
    Double lowerRange = 10 + ctr * .1;
    Console.WriteLine("{0:N1} to {1:N1}: {2,8:N0}  ({3,7:P2})",
                      lowerRange, lowerRange + .1, range[ctr],
                      range[ctr] / 1000000.0);
}

// The example displays output like the following:
//       10.0 to 10.1:   99,929  ( 9.99 %)
//       10.1 to 10.2:  100,189  (10.02 %)
//       10.2 to 10.3:   99,384  ( 9.94 %)
//       10.3 to 10.4:  100,240  (10.02 %)
//       10.4 to 10.5:   99,397  ( 9.94 %)
//       10.5 to 10.6:  100,580  (10.06 %)
//       10.6 to 10.7:  100,293  (10.03 %)
//       10.7 to 10.8:  100,135  (10.01 %)
//       10.8 to 10.9:   99,905  ( 9.99 %)
//       10.9 to 11.0:   99,948  ( 9.99 %)
let rnd = Random()

let lowerBound = 10.0
let upperBound = 11.0

let range =
    Array.init 1000000 (fun _ -> rnd.NextDouble() * (upperBound - lowerBound) +  lowerBound)
    |> Array.countBy (fun x -> Math.Truncate((x - lowerBound) * 10.0) |> int)
    |> Array.map snd

for i = 0 to 9 do 
    let lowerRange = 10.0 + float i * 0.1
    printfn $"{lowerRange:N1} to {lowerRange + 0.1:N1}: {range.[i],8:N0}  ({float range.[i] / 1000000.0,6:P2})"

// The example displays output like the following:
//       10.0 to 10.1:   99,929  ( 9.99 %)
//       10.1 to 10.2:  100,189  (10.02 %)
//       10.2 to 10.3:   99,384  ( 9.94 %)
//       10.3 to 10.4:  100,240  (10.02 %)
//       10.4 to 10.5:   99,397  ( 9.94 %)
//       10.5 to 10.6:  100,580  (10.06 %)
//       10.6 to 10.7:  100,293  (10.03 %)
//       10.7 to 10.8:  100,135  (10.01 %)
//       10.8 to 10.9:   99,905  ( 9.99 %)
//       10.9 to 11.0:   99,948  ( 9.99 %)
Module Example7
    Public Sub Main()
        Dim rnd As New Random()
        Dim lowerBound As Integer = 10
        Dim upperBound As Integer = 11
        Dim range(9) As Integer
        For ctr As Integer = 1 To 1000000
            Dim value As Double = rnd.NextDouble() * (upperBound - lowerBound) + lowerBound
            range(CInt(Math.Truncate((value - lowerBound) * 10))) += 1
        Next

        For ctr As Integer = 0 To 9
            Dim lowerRange As Double = 10 + ctr * 0.1
            Console.WriteLine("{0:N1} to {1:N1}: {2,8:N0}  ({3,7:P2})",
                           lowerRange, lowerRange + 0.1, range(ctr),
                           range(ctr) / 1000000.0)
        Next
    End Sub
End Module
' The example displays output like the following:
'       10.0 to 10.1:   99,929  ( 9.99 %)
'       10.1 to 10.2:  100,189  (10.02 %)
'       10.2 to 10.3:   99,384  ( 9.94 %)
'       10.3 to 10.4:  100,240  (10.02 %)
'       10.4 to 10.5:   99,397  ( 9.94 %)
'       10.5 to 10.6:  100,580  (10.06 %)
'       10.6 to 10.7:  100,293  (10.03 %)
'       10.7 to 10.8:  100,135  (10.01 %)
'       10.8 to 10.9:   99,905  ( 9.99 %)
'       10.9 to 11.0:   99,948  ( 9.99 %)

產生隨機布爾值

類別 Random 不提供產生 Boolean 值的方法。 不過,您可以定義自己的類別或方法來執行此動作。 下列範例會使用單一方法NextBoolean定義 類別 BooleanGenerator。 類別 BooleanGenerator 會將 Random 物件儲存為私用變數。 方法 NextBoolean 會呼叫 方法, Random.Next(Int32, Int32) 並將結果傳遞至 Convert.ToBoolean(Int32) 方法。 請注意,2 是用來指定隨機數上限的自變數。 由於這是獨佔值,因此方法呼叫會傳回 0 或 1。

using System;

public class Example1
{
    public static void Main()
    {
        // Instantiate the Boolean generator.
        BooleanGenerator boolGen = new BooleanGenerator();
        int totalTrue = 0, totalFalse = 0;

        // Generate 1,0000 random Booleans, and keep a running total.
        for (int ctr = 0; ctr < 1000000; ctr++)
        {
            bool value = boolGen.NextBoolean();
            if (value)
                totalTrue++;
            else
                totalFalse++;
        }
        Console.WriteLine("Number of true values:  {0,7:N0} ({1:P3})",
                          totalTrue,
                          ((double)totalTrue) / (totalTrue + totalFalse));
        Console.WriteLine("Number of false values: {0,7:N0} ({1:P3})",
                          totalFalse,
                          ((double)totalFalse) / (totalTrue + totalFalse));
    }
}

public class BooleanGenerator
{
    Random rnd;

    public BooleanGenerator()
    {
        rnd = new Random();
    }

    public bool NextBoolean()
    {
        return rnd.Next(0, 2) == 1;
    }
}
// The example displays output like the following:
//       Number of true values:  500,004 (50.000 %)
//       Number of false values: 499,996 (50.000 %)
open System

type BooleanGenerator() =
    let rnd = Random()

    member _.NextBoolean() =
        rnd.Next(0, 2) = 1

let boolGen = BooleanGenerator()
let mutable totalTrue, totalFalse = 0, 0

for _ = 1 to 1000000 do
    let value = boolGen.NextBoolean()
    if value then 
        totalTrue <- totalTrue + 1
    else 
        totalFalse <- totalFalse + 1

printfn $"Number of true values:  {totalTrue,7:N0} ({(double totalTrue) / double (totalTrue + totalFalse):P3})"
printfn $"Number of false values: {totalFalse,7:N0} ({(double totalFalse) / double (totalTrue + totalFalse):P3})"

// The example displays output like the following:
//       Number of true values:  500,004 (50.000 %)
//       Number of false values: 499,996 (50.000 %)
Module Example2
    Public Sub Main()
        ' Instantiate the Boolean generator.
        Dim boolGen As New BooleanGenerator()
        Dim totalTrue, totalFalse As Integer

        ' Generate 1,0000 random Booleans, and keep a running total.
        For ctr As Integer = 0 To 9999999
            Dim value As Boolean = boolGen.NextBoolean()
            If value Then
                totalTrue += 1
            Else
                totalFalse += 1
            End If
        Next
        Console.WriteLine("Number of true values:  {0,7:N0} ({1:P3})",
                        totalTrue,
                        totalTrue / (totalTrue + totalFalse))
        Console.WriteLine("Number of false values: {0,7:N0} ({1:P3})",
                        totalFalse,
                        totalFalse / (totalTrue + totalFalse))
    End Sub
End Module

Public Class BooleanGenerator
   Dim rnd As Random
   
   Public Sub New()
      rnd = New Random()
   End Sub

   Public Function NextBoolean() As Boolean
      Return Convert.ToBoolean(rnd.Next(0, 2))
   End Function
End Class
' The example displays the following output:
'       Number of true values:  500,004 (50.000 %)
'       Number of false values: 499,996 (50.000 %)

此範例可能只是定義單一方法,而不是建立個別類別來產生隨機 Boolean 值。 不過,在此情況下, Random 對象應該已定義為類別層級變數,以避免在每個方法呼叫中具現化新的 Random 實例。 在 Visual Basic 中,Random 實例可以定義為 方法中的NextBoolean靜態變數。 下列範例提供 實作。

Random rnd = new Random();

int totalTrue = 0, totalFalse = 0;

// Generate 1,000,000 random Booleans, and keep a running total.
for (int ctr = 0; ctr < 1000000; ctr++)
{
    bool value = NextBoolean();
    if (value)
        totalTrue++;
    else
        totalFalse++;
}
Console.WriteLine("Number of true values:  {0,7:N0} ({1:P3})",
                  totalTrue,
                  ((double)totalTrue) / (totalTrue + totalFalse));
Console.WriteLine("Number of false values: {0,7:N0} ({1:P3})",
                  totalFalse,
                  ((double)totalFalse) / (totalTrue + totalFalse));

bool NextBoolean()
{
    return rnd.Next(0, 2) == 1;
}

// The example displays output like the following:
//       Number of true values:  499,777 (49.978 %)
//       Number of false values: 500,223 (50.022 %)
let rnd = Random()

let nextBool () =
    rnd.Next(0, 2) = 1

let mutable totalTrue, totalFalse = 0, 0

for _ = 1 to 1000000 do
    let value = nextBool ()
    if value then 
        totalTrue <- totalTrue + 1
    else 
        totalFalse <- totalFalse + 1

printfn $"Number of true values:  {totalTrue,7:N0} ({(double totalTrue) / double (totalTrue + totalFalse):P3})"
printfn $"Number of false values: {totalFalse,7:N0} ({(double totalFalse) / double (totalTrue + totalFalse):P3})"

// The example displays output like the following:
//       Number of true values:  499,777 (49.978 %)
//       Number of false values: 500,223 (50.022 %)
Module Example3
    Public Sub Main()
        Dim totalTrue, totalFalse As Integer

        ' Generate 1,0000 random Booleans, and keep a running total.
        For ctr As Integer = 0 To 9999999
            Dim value As Boolean = NextBoolean()
            If value Then
                totalTrue += 1
            Else
                totalFalse += 1
            End If
        Next
        Console.WriteLine("Number of true values:  {0,7:N0} ({1:P3})",
                        totalTrue,
                        totalTrue / (totalTrue + totalFalse))
        Console.WriteLine("Number of false values: {0,7:N0} ({1:P3})",
                        totalFalse,
                        totalFalse / (totalTrue + totalFalse))
    End Sub

    Public Function NextBoolean() As Boolean
        Static rnd As New Random()
        Return Convert.ToBoolean(rnd.Next(0, 2))
    End Function
End Module
' The example displays the following output:
'       Number of true values:  499,777 (49.978 %)
'       Number of false values: 500,223 (50.022 %)

產生隨機 64 位整數

方法的多 Next 載會傳回 32 位整數。 不過,在某些情況下,您可能想要使用64位整數。 您可以依照下列方式來執行這項操作:

  1. NextDouble呼叫 方法來擷取雙精確度浮點值。

  2. 將這個值乘以 Int64.MaxValue

下列範例會使用這項技術來產生 2000 萬個隨機長整數,並將其分類為 10 個相等群組。 然後,它會計算每個群組中的數位從 0 到 Int64.MaxValue,以評估隨機數的分佈。 如範例的輸出所示,數位會透過長整數的範圍平均分配。

const long ONE_TENTH = 922337203685477581;

Random rnd = new Random();
long number;
int[] count = new int[10];

// Generate 20 million long integers.
for (int ctr = 1; ctr <= 20000000; ctr++)
{
    number = (long)(rnd.NextDouble() * Int64.MaxValue);
    // Categorize random numbers.
    count[(int)(number / ONE_TENTH)]++;
}
// Display breakdown by range.
Console.WriteLine("{0,28} {1,32}   {2,7}\n", "Range", "Count", "Pct.");
for (int ctr = 0; ctr <= 9; ctr++)
    Console.WriteLine("{0,25:N0}-{1,25:N0}  {2,8:N0}   {3,7:P2}", ctr * ONE_TENTH,
                       ctr < 9 ? ctr * ONE_TENTH + ONE_TENTH - 1 : Int64.MaxValue,
                       count[ctr], count[ctr] / 20000000.0);

// The example displays output like the following:
//                           Range                            Count      Pct.
//
//                            0-  922,337,203,685,477,580  1,996,148    9.98 %
//      922,337,203,685,477,581-1,844,674,407,370,955,161  2,000,293   10.00 %
//    1,844,674,407,370,955,162-2,767,011,611,056,432,742  2,000,094   10.00 %
//    2,767,011,611,056,432,743-3,689,348,814,741,910,323  2,000,159   10.00 %
//    3,689,348,814,741,910,324-4,611,686,018,427,387,904  1,999,552   10.00 %
//    4,611,686,018,427,387,905-5,534,023,222,112,865,485  1,998,248    9.99 %
//    5,534,023,222,112,865,486-6,456,360,425,798,343,066  2,000,696   10.00 %
//    6,456,360,425,798,343,067-7,378,697,629,483,820,647  2,001,637   10.01 %
//    7,378,697,629,483,820,648-8,301,034,833,169,298,228  2,002,870   10.01 %
//    8,301,034,833,169,298,229-9,223,372,036,854,775,807  2,000,303   10.00 %
[<Literal>]
let ONE_TENTH = 922337203685477581L

let rnd = Random()

let count =
    // Generate 20 million random long integers.
    Array.init 20000000 (fun _ -> rnd.NextDouble() * (float Int64.MaxValue) |> int64 )
    |> Array.countBy (fun x -> x / ONE_TENTH) // Categorize and count random numbers.
    |> Array.map snd

// Display breakdown by range.
printfn "%28s %32s   %7s\n" "Range" "Count" "Pct."
for i = 0 to 9 do
    let r1 = int64 i * ONE_TENTH
    let r2 = if i < 9 then r1 + ONE_TENTH - 1L else Int64.MaxValue
    printfn $"{r1,25:N0}-{r2,25:N0}  {count.[i],8:N0}   {float count.[i] / 20000000.0,7:P2}"

// The example displays output like the following:
//                           Range                            Count      Pct.
//
//                            0-  922,337,203,685,477,580  1,996,148    9.98 %
//      922,337,203,685,477,581-1,844,674,407,370,955,161  2,000,293   10.00 %
//    1,844,674,407,370,955,162-2,767,011,611,056,432,742  2,000,094   10.00 %
//    2,767,011,611,056,432,743-3,689,348,814,741,910,323  2,000,159   10.00 %
//    3,689,348,814,741,910,324-4,611,686,018,427,387,904  1,999,552   10.00 %
//    4,611,686,018,427,387,905-5,534,023,222,112,865,485  1,998,248    9.99 %
//    5,534,023,222,112,865,486-6,456,360,425,798,343,066  2,000,696   10.00 %
//    6,456,360,425,798,343,067-7,378,697,629,483,820,647  2,001,637   10.01 %
//    7,378,697,629,483,820,648-8,301,034,833,169,298,228  2,002,870   10.01 %
//    8,301,034,833,169,298,229-9,223,372,036,854,775,807  2,000,303   10.00 %
Module Example8
    Public Sub Main()
        Const ONE_TENTH As Long = 922337203685477581

        Dim rnd As New Random()
        Dim number As Long
        Dim count(9) As Integer

        ' Generate 20 million long integers.
        For ctr As Integer = 1 To 20000000
            number = CLng(rnd.NextDouble() * Int64.MaxValue)
            ' Categorize random numbers.
            count(CInt(number \ ONE_TENTH)) += 1
        Next
        ' Display breakdown by range.
        Console.WriteLine("{0,28} {1,32}   {2,7}", "Range", "Count", "Pct.")
        Console.WriteLine()
        For ctr As Integer = 0 To 9
            Console.WriteLine("{0,25:N0}-{1,25:N0}  {2,8:N0}   {3,7:P2}", ctr * ONE_TENTH,
                            If(ctr < 9, ctr * ONE_TENTH + ONE_TENTH - 1, Int64.MaxValue),
                            count(ctr), count(ctr) / 20000000)
        Next
    End Sub
End Module
' The example displays output like the following:
'                           Range                            Count      Pct.
'    
'                            0-  922,337,203,685,477,580  1,996,148    9.98 %
'      922,337,203,685,477,581-1,844,674,407,370,955,161  2,000,293   10.00 %
'    1,844,674,407,370,955,162-2,767,011,611,056,432,742  2,000,094   10.00 %
'    2,767,011,611,056,432,743-3,689,348,814,741,910,323  2,000,159   10.00 %
'    3,689,348,814,741,910,324-4,611,686,018,427,387,904  1,999,552   10.00 %
'    4,611,686,018,427,387,905-5,534,023,222,112,865,485  1,998,248    9.99 %
'    5,534,023,222,112,865,486-6,456,360,425,798,343,066  2,000,696   10.00 %
'    6,456,360,425,798,343,067-7,378,697,629,483,820,647  2,001,637   10.01 %
'    7,378,697,629,483,820,648-8,301,034,833,169,298,228  2,002,870   10.01 %
'    8,301,034,833,169,298,229-9,223,372,036,854,775,807  2,000,303   10.00 %

使用位操作的替代技術不會產生真正的隨機數。 這項技術會呼叫 Next() 來產生兩個整數、一個乘以 32 位的左移,以及一起產生 OR。 這項技術有兩個限制:

  1. 因為位 31 是正負號位,因此產生的長整數位 31 中的值一律為 0。 藉由產生隨機 0 或 1、將它向左移 31 位,並使用原始隨機長整數進行 ORing,即可解決此問題。

  2. 更嚴重的是,因為所 Next() 傳回值的機率會是0,如果範圍中的任何隨機數0x0-0x00000000FFFFFFFF則很少。

擷取指定範圍內的位元組

方法的多 Next 載可讓您指定隨機數的範圍,但 NextBytes 方法不會。 下列範例會實作 NextBytes 方法,讓您指定傳回位元組的範圍。 它會定義 Random2 衍生自 Random 的類別,並多載其 NextBytes 方法。

using System;

public class Example3
{
    public static void Main()
    {
        Random2 rnd = new Random2();
        Byte[] bytes = new Byte[10000];
        int[] total = new int[101];
        rnd.NextBytes(bytes, 0, 101);

        // Calculate how many of each value we have.
        foreach (var value in bytes)
            total[value]++;

        // Display the results.
        for (int ctr = 0; ctr < total.Length; ctr++)
        {
            Console.Write("{0,3}: {1,-3}   ", ctr, total[ctr]);
            if ((ctr + 1) % 5 == 0) Console.WriteLine();
        }
    }
}

public class Random2 : Random
{
    public Random2() : base()
    { }

    public Random2(int seed) : base(seed)
    { }

    public void NextBytes(byte[] bytes, byte minValue, byte maxValue)
    {
        for (int ctr = bytes.GetLowerBound(0); ctr <= bytes.GetUpperBound(0); ctr++)
            bytes[ctr] = (byte)Next(minValue, maxValue);
    }
}
// The example displays output like the following:
//         0: 115     1: 119     2: 92      3: 98      4: 92
//         5: 102     6: 103     7: 84      8: 93      9: 116
//        10: 91     11: 98     12: 106    13: 91     14: 92
//        15: 101    16: 100    17: 96     18: 97     19: 100
//        20: 101    21: 106    22: 112    23: 82     24: 85
//        25: 102    26: 107    27: 98     28: 106    29: 102
//        30: 109    31: 108    32: 94     33: 101    34: 107
//        35: 101    36: 86     37: 100    38: 101    39: 102
//        40: 113    41: 95     42: 96     43: 89     44: 99
//        45: 81     46: 89     47: 105    48: 100    49: 85
//        50: 103    51: 103    52: 93     53: 89     54: 91
//        55: 97     56: 105    57: 97     58: 110    59: 86
//        60: 116    61: 94     62: 117    63: 98     64: 110
//        65: 93     66: 102    67: 100    68: 105    69: 83
//        70: 81     71: 97     72: 85     73: 70     74: 98
//        75: 100    76: 110    77: 114    78: 83     79: 90
//        80: 96     81: 112    82: 102    83: 102    84: 99
//        85: 81     86: 100    87: 93     88: 99     89: 118
//        90: 95     91: 124    92: 108    93: 96     94: 104
//        95: 106    96: 99     97: 99     98: 92     99: 99
//       100: 108
open System

type Random2() =
    inherit Random()

    member this.NextBytes(bytes: byte[], minValue: byte, maxValue: byte) =
        for i=bytes.GetLowerBound(0) to bytes.GetUpperBound(0) do
            bytes.[i] <- this.Next(int minValue, int maxValue) |> byte

let rnd = Random2()
let bytes = Array.zeroCreate 10000
let total = Array.zeroCreate 101
rnd.NextBytes(bytes, 0uy, 101uy)

// Calculate how many of each value we have.
for v in bytes do 
    total.[int v] <- total.[int v] + 1

// Display the results.
for i = 0 to total.Length - 1 do
    printf "%3i: %-3i   " i total.[i]
    if (i + 1) % 5 = 0 then printfn ""

// The example displays output like the following:
//         0: 115     1: 119     2: 92      3: 98      4: 92
//         5: 102     6: 103     7: 84      8: 93      9: 116
//        10: 91     11: 98     12: 106    13: 91     14: 92
//        15: 101    16: 100    17: 96     18: 97     19: 100
//        20: 101    21: 106    22: 112    23: 82     24: 85
//        25: 102    26: 107    27: 98     28: 106    29: 102
//        30: 109    31: 108    32: 94     33: 101    34: 107
//        35: 101    36: 86     37: 100    38: 101    39: 102
//        40: 113    41: 95     42: 96     43: 89     44: 99
//        45: 81     46: 89     47: 105    48: 100    49: 85
//        50: 103    51: 103    52: 93     53: 89     54: 91
//        55: 97     56: 105    57: 97     58: 110    59: 86
//        60: 116    61: 94     62: 117    63: 98     64: 110
//        65: 93     66: 102    67: 100    68: 105    69: 83
//        70: 81     71: 97     72: 85     73: 70     74: 98
//        75: 100    76: 110    77: 114    78: 83     79: 90
//        80: 96     81: 112    82: 102    83: 102    84: 99
//        85: 81     86: 100    87: 93     88: 99     89: 118
//        90: 95     91: 124    92: 108    93: 96     94: 104
//        95: 106    96: 99     97: 99     98: 92     99: 99
//       100: 108
Module Example4
    Public Sub Main()
        Dim rnd As New Random2()
        Dim bytes(9999) As Byte
        Dim total(100) As Integer
        rnd.NextBytes(bytes, 0, 101)

        ' Calculate how many of each value we have.
        For Each value In bytes
            total(value) += 1
        Next

        ' Display the results.
        For ctr As Integer = 0 To total.Length - 1
            Console.Write("{0,3}: {1,-3}   ", ctr, total(ctr))
            If (ctr + 1) Mod 5 = 0 Then Console.WriteLine()
        Next
    End Sub
End Module

Public Class Random2 : Inherits Random
   Public Sub New()
      MyBase.New()
   End Sub   

   Public Sub New(seed As Integer)
      MyBase.New(seed)
   End Sub

   Public Overloads Sub NextBytes(bytes() As Byte, 
                                  minValue As Byte, maxValue As Byte)
      For ctr As Integer = bytes.GetLowerbound(0) To bytes.GetUpperBound(0)
         bytes(ctr) = CByte(MyBase.Next(minValue, maxValue))
      Next
   End Sub
End Class 
' The example displays output like the following:
'         0: 115     1: 119     2: 92      3: 98      4: 92
'         5: 102     6: 103     7: 84      8: 93      9: 116
'        10: 91     11: 98     12: 106    13: 91     14: 92
'        15: 101    16: 100    17: 96     18: 97     19: 100
'        20: 101    21: 106    22: 112    23: 82     24: 85
'        25: 102    26: 107    27: 98     28: 106    29: 102
'        30: 109    31: 108    32: 94     33: 101    34: 107
'        35: 101    36: 86     37: 100    38: 101    39: 102
'        40: 113    41: 95     42: 96     43: 89     44: 99
'        45: 81     46: 89     47: 105    48: 100    49: 85
'        50: 103    51: 103    52: 93     53: 89     54: 91
'        55: 97     56: 105    57: 97     58: 110    59: 86
'        60: 116    61: 94     62: 117    63: 98     64: 110
'        65: 93     66: 102    67: 100    68: 105    69: 83
'        70: 81     71: 97     72: 85     73: 70     74: 98
'        75: 100    76: 110    77: 114    78: 83     79: 90
'        80: 96     81: 112    82: 102    83: 102    84: 99
'        85: 81     86: 100    87: 93     88: 99     89: 118
'        90: 95     91: 124    92: 108    93: 96     94: 104
'        95: 106    96: 99     97: 99     98: 92     99: 99
'       100: 108

方法 NextBytes(Byte[], Byte, Byte) 會包裝對 方法的 Next(Int32, Int32) 呼叫,並指定我們想要在位元組陣列中傳回的最小值和最大值(在此案例中為0和101)。 由於我們確定 方法傳 Next 回的 Byte 整數值在數據類型的範圍內,因此我們可以安全地轉換它們(在 C# 和 F# 中),或將它們從整數轉換成位元組。

從陣列或集合隨機擷取專案

隨機數通常做為索引,從數位或集合擷取值。 若要擷取隨機索引值,您可以呼叫 Next(Int32, Int32) 方法,並使用陣列的下限做為其自變數的值 minValue ,而其中一個大於陣列的上限做為其自變數的值 maxValue 。 如果是以零起始的陣列,這相當於其 Length 屬性,或大於方法傳 Array.GetUpperBound 回的值。 下列範例會從城市數位隨機擷取 美國 中的城市名稱。

String[] cities = { "Atlanta", "Boston", "Chicago", "Detroit",
                    "Fort Wayne", "Greensboro", "Honolulu", "Indianapolis",
                    "Jersey City", "Kansas City", "Los Angeles",
                    "Milwaukee", "New York", "Omaha", "Philadelphia",
                    "Raleigh", "San Francisco", "Tulsa", "Washington" };
Random rnd = new Random();
int index = rnd.Next(0, cities.Length);
Console.WriteLine("Today's city of the day: {0}",
                  cities[index]);

// The example displays output like the following:
//   Today's city of the day: Honolulu
let cities = 
    [| "Atlanta"; "Boston"; "Chicago"; "Detroit";
       "Fort Wayne"; "Greensboro"; "Honolulu"; "Indianapolis";
       "Jersey City"; "Kansas City"; "Los Angeles";
       "Milwaukee"; "New York"; "Omaha"; "Philadelphia";
       "Raleigh"; "San Francisco"; "Tulsa"; "Washington" |]

let rnd = Random()

let index = rnd.Next(0,cities.Length)

printfn "Today's city of the day: %s" cities.[index]

// The example displays output like the following:
//   Today's city of the day: Honolulu
Module Example1
    Public Sub Main()
        Dim cities() As String = {"Atlanta", "Boston", "Chicago", "Detroit",
                                 "Fort Wayne", "Greensboro", "Honolulu", "Indianapolis",
                                 "Jersey City", "Kansas City", "Los Angeles",
                                 "Milwaukee", "New York", "Omaha", "Philadelphia",
                                 "Raleigh", "San Francisco", "Tulsa", "Washington"}
        Dim rnd As New Random()
        Dim index As Integer = rnd.Next(0, cities.Length)
        Console.WriteLine("Today's city of the day: {0}",
                        cities(index))
    End Sub
End Module
' The example displays output like the following:
'   Today's city of the day: Honolulu

從陣列或集合擷取唯一專案

隨機數產生器一律可以傳回重複的值。 當數位範圍變小或產生的值數目變大時,重複專案的機率就會增加。 如果隨機值必須是唯一的,則會產生更多數位來補償重複項目,導致效能越來越差。

有數種處理此案例的技術。 其中一個常見解決方案是建立數位或集合,其中包含要擷取的值,以及包含隨機浮點數的平行數組。 第二個陣列會在第一個數位建立時填入隨機數,而且 Array.Sort(Array, Array) 方法會使用平行陣列中的值來排序第一個數位。

例如,如果您正在開發單打遊戲,您想要確保每張卡片只使用一次。 您可以建立可用來排序牌組的平行隨機數陣列,而不是產生隨機數來擷取卡片並追蹤該卡片是否已處理。 排序牌組之後,您的應用程式可以維護指標,以指出牌組上下一張卡片的索引。

下列範例將示範這個方法。 它會定義代表 Card 打牌的類別,以及 Dealer 處理一組隨機卡片的類別。 類別建Dealer構函式會填入兩個deck數位:具有類別範圍的陣列,代表牌組中的所有卡片;以及具有與陣列相同數目元素deck的本機order數位,並且會填入隨機產生的Double值。 接著會Array.Sort(Array, Array)呼叫 方法,根據陣列中的order值來排序deck陣列。

using System;

// A class that represents an individual card in a playing deck.
public class Card
{
    public Suit Suit;
    public FaceValue FaceValue;

    public override String ToString()
    {
        return String.Format("{0:F} of {1:F}", this.FaceValue, this.Suit);
    }
}

public enum Suit { Hearts, Diamonds, Spades, Clubs };

public enum FaceValue
{
    Ace = 1, Two, Three, Four, Five, Six,
    Seven, Eight, Nine, Ten, Jack, Queen,
    King
};

public class Dealer
{
    Random rnd;
    // A deck of cards, without Jokers.
    Card[] deck = new Card[52];
    // Parallel array for sorting cards.
    Double[] order = new Double[52];
    // A pointer to the next card to deal.
    int ptr = 0;
    // A flag to indicate the deck is used.
    bool mustReshuffle = false;

    public Dealer()
    {
        rnd = new Random();
        // Initialize the deck.
        int deckCtr = 0;
        foreach (var suit in Enum.GetValues(typeof(Suit)))
        {
            foreach (var faceValue in Enum.GetValues(typeof(FaceValue)))
            {
                Card card = new Card();
                card.Suit = (Suit)suit;
                card.FaceValue = (FaceValue)faceValue;
                deck[deckCtr] = card;
                deckCtr++;
            }
        }

        for (int ctr = 0; ctr < order.Length; ctr++)
            order[ctr] = rnd.NextDouble();

        Array.Sort(order, deck);
    }

    public Card[] Deal(int numberToDeal)
    {
        if (mustReshuffle)
        {
            Console.WriteLine("There are no cards left in the deck");
            return null;
        }

        Card[] cardsDealt = new Card[numberToDeal];
        for (int ctr = 0; ctr < numberToDeal; ctr++)
        {
            cardsDealt[ctr] = deck[ptr];
            ptr++;
            if (ptr == deck.Length)
                mustReshuffle = true;

            if (mustReshuffle & ctr < numberToDeal - 1)
            {
                Console.WriteLine("Can only deal the {0} cards remaining on the deck.",
                                  ctr + 1);
                return cardsDealt;
            }
        }
        return cardsDealt;
    }
}

public class Example17
{
    public static void Main()
    {
        Dealer dealer = new Dealer();
        ShowCards(dealer.Deal(20));
    }

    private static void ShowCards(Card[] cards)
    {
        foreach (var card in cards)
            if (card != null)
                Console.WriteLine("{0} of {1}", card.FaceValue, card.Suit);
    }
}
// The example displays output like the following:
//       Six of Diamonds
//       King of Clubs
//       Eight of Clubs
//       Seven of Clubs
//       Queen of Clubs
//       King of Hearts
//       Three of Spades
//       Ace of Clubs
//       Four of Hearts
//       Three of Diamonds
//       Nine of Diamonds
//       Two of Hearts
//       Ace of Hearts
//       Three of Hearts
//       Four of Spades
//       Eight of Hearts
//       Queen of Diamonds
//       Two of Clubs
//       Four of Diamonds
//       Jack of Hearts
open System

type Suit =
    | Clubs
    | Diamonds
    | Hearts
    | Spades

type Face =
    | Ace | Two | Three
    | Four | Five | Six
    | Seven | Eight | Nine
    | Ten | Jack | Queen | King

type Card = { Face: Face; Suit: Suit }

let suits = [ Clubs; Diamonds; Hearts; Spades ]
let faces = [ Ace; Two; Three; Four; Five; Six; Seven; Eight; Nine; Ten; Jack; Queen; King ]

type Dealer() =
    let rnd = Random()
    let mutable pos = 0
    // Parallel array for sorting cards.
    let order = Array.init (suits.Length * faces.Length) (fun _ -> rnd.NextDouble() )
    // A deck of cards, without Jokers.
    let deck = [|
        for s in suits do
            for f in faces do
                { Face = f; Suit = s } |]
    // Shuffle the deck.
    do Array.Sort(order, deck)

    // Deal a number of cards from the deck, return None if failed
    member _.Deal(numberToDeal) : Card [] option = 
        if numberToDeal = 0 || pos = deck.Length then
            printfn "There are no cards left in the deck"
            None
        else 
            let cards = deck.[pos .. numberToDeal + pos - 1]
            if numberToDeal > deck.Length - pos then
                printfn "Can only deal the %i cards remaining on the deck." (deck.Length - pos)
            pos <- min (pos + numberToDeal) deck.Length
            Some cards

let showCards cards = 
    for card in cards do
        printfn $"{card.Face} of {card.Suit}"

let dealer = Dealer()

dealer.Deal 20
|> Option.iter showCards

// The example displays output like the following:
//       Six of Diamonds
//       King of Clubs
//       Eight of Clubs
//       Seven of Clubs
//       Queen of Clubs
//       King of Hearts
//       Three of Spades
//       Ace of Clubs
//       Four of Hearts
//       Three of Diamonds
//       Nine of Diamonds
//       Two of Hearts
//       Ace of Hearts
//       Three of Hearts
//       Four of Spades
//       Eight of Hearts
//       Queen of Diamonds
//       Two of Clubs
//       Four of Diamonds
//       Jack of Hearts
' A class that represents an individual card in a playing deck.
Public Class Card
   Public Suit As Suit
   Public FaceValue As FaceValue
   
   Public Overrides Function ToString() As String
      Return String.Format("{0:F} of {1:F}", Me.FaceValue, Me.Suit)
   End Function
End Class

Public Enum Suit As Integer
   Hearts = 0
   Diamonds = 1
   Spades = 2
   Clubs = 3
End Enum

Public Enum FaceValue As Integer
   Ace = 1
   Two = 2
   Three = 3
   Four = 4
   Five = 5
   Six = 6
   Seven = 7
   Eight = 8
   Nine = 9
   Ten = 10
   Jack = 11
   Queen = 12
   King = 13
End Enum

Public Class Dealer
   Dim rnd As Random
   ' A deck of cards, without Jokers.
   Dim deck(51) As Card
   ' Parallel array for sorting cards.
   Dim order(51) As Double
   ' A pointer to the next card to deal.
   Dim ptr As Integer = 0
   ' A flag to indicate the deck is used.
   Dim mustReshuffle As Boolean
   
   Public Sub New()
      rnd = New Random()
      ' Initialize the deck.
      Dim deckCtr As Integer = 0
      For Each Suit In [Enum].GetValues(GetType(Suit))
         For Each faceValue In [Enum].GetValues(GetType(FaceValue))
            Dim card As New Card()
            card.Suit = CType(Suit, Suit)
            card.FaceValue = CType(faceValue, FaceValue)
            deck(deckCtr) = card  
            deckCtr += 1
         Next
      Next
      For ctr As Integer = 0 To order.Length - 1
         order(ctr) = rnd.NextDouble()   
      Next   
      Array.Sort(order, deck)
   End Sub

   Public Function Deal(numberToDeal As Integer) As Card()
      If mustReshuffle Then
         Console.WriteLine("There are no cards left in the deck")
         Return Nothing
      End If
      
      Dim cardsDealt(numberToDeal - 1) As Card
      For ctr As Integer = 0 To numberToDeal - 1
         cardsDealt(ctr) = deck(ptr)
         ptr += 1
         If ptr = deck.Length Then 
            mustReshuffle = True
         End If
         If mustReshuffle And ctr < numberToDeal - 1
            Console.WriteLine("Can only deal the {0} cards remaining on the deck.", 
                              ctr + 1)
            Return cardsDealt
         End If
      Next
      Return cardsDealt
   End Function
End Class

Public Module Example
   Public Sub Main()
      Dim dealer As New Dealer()
      ShowCards(dealer.Deal(20))
   End Sub
   
   Private Sub ShowCards(cards() As Card)
      For Each card In cards
         If card IsNot Nothing Then _
            Console.WriteLine("{0} of {1}", card.FaceValue, card.Suit)
      Next
   End Sub
End Module
' The example displays output like the following:
'       Six of Diamonds
'       King of Clubs
'       Eight of Clubs
'       Seven of Clubs
'       Queen of Clubs
'       King of Hearts
'       Three of Spades
'       Ace of Clubs
'       Four of Hearts
'       Three of Diamonds
'       Nine of Diamonds
'       Two of Hearts
'       Ace of Hearts
'       Three of Hearts
'       Four of Spades
'       Eight of Hearts
'       Queen of Diamonds
'       Two of Clubs
'       Four of Diamonds
'       Jack of Hearts