Random 類別

定義

代表一種偽隨機數產生器,這是一種產生符合特定統計隨機性要求的數字序列的演算法。

public ref class Random
public class Random
[System.Serializable]
public class Random
[System.Serializable]
[System.Runtime.InteropServices.ComVisible(true)]
public class Random
type Random = class
[<System.Serializable>]
type Random = class
[<System.Serializable>]
[<System.Runtime.InteropServices.ComVisible(true)>]
type Random = class
Public Class Random
繼承
Random
屬性

範例

以下範例建立一個單一隨機數產生器,並呼叫其 NextBytesNext,以及 NextDouble 產生不同範圍內隨機數序列的方法。

// Instantiate random number generator using system-supplied value as seed.
var rand = new Random();

// Generate and display 5 random byte (integer) values.
byte[] bytes = new byte[5];
rand.NextBytes(bytes);
Console.WriteLine("Five random byte values:");
foreach (byte byteValue in bytes)
    Console.Write("{0, 5}", byteValue);
Console.WriteLine();

// Generate and display 5 random integers.
Console.WriteLine("Five random integer values:");
for (int ctr = 0; ctr <= 4; ctr++)
    Console.Write("{0,15:N0}", rand.Next());
Console.WriteLine();

// Generate and display 5 random integers between 0 and 100.
Console.WriteLine("Five random integers between 0 and 100:");
for (int ctr = 0; ctr <= 4; ctr++)
    Console.Write("{0,8:N0}", rand.Next(101));
Console.WriteLine();

// Generate and display 5 random integers from 50 to 100.
Console.WriteLine("Five random integers between 50 and 100:");
for (int ctr = 0; ctr <= 4; ctr++)
    Console.Write("{0,8:N0}", rand.Next(50, 101));
Console.WriteLine();

// Generate and display 5 random floating point values from 0 to 1.
Console.WriteLine("Five Doubles.");
for (int ctr = 0; ctr <= 4; ctr++)
    Console.Write("{0,8:N3}", rand.NextDouble());
Console.WriteLine();

// Generate and display 5 random floating point values from 0 to 5.
Console.WriteLine("Five Doubles between 0 and 5.");
for (int ctr = 0; ctr <= 4; ctr++)
    Console.Write("{0,8:N3}", rand.NextDouble() * 5);

// The example displays output like the following:
//    Five random byte values:
//      194  185  239   54  116
//    Five random integer values:
//        507,353,531  1,509,532,693  2,125,074,958  1,409,512,757    652,767,128
//    Five random integers between 0 and 100:
//          16      78      94      79      52
//    Five random integers between 50 and 100:
//          56      66      96      60      65
//    Five Doubles.
//       0.943   0.108   0.744   0.563   0.415
//    Five Doubles between 0 and 5.
//       2.934   3.130   0.292   1.432   4.369
// Instantiate random number generator using system-supplied value as seed.
let rand = Random()

// Generate and display 5 random byte (integer) values.
let bytes = Array.zeroCreate 5
rand.NextBytes bytes
printfn "Five random byte values:"
for byte in bytes do
    printf "%5i" byte
printfn ""

// Generate and display 5 random integers.
printfn "Five random integer values:"
for _ = 0 to 4 do
    printf $"{rand.Next(),15:N0}" 
printfn ""

// Generate and display 5 random integers between 0 and 100.
printfn "Five random integers between 0 and 100:"
for _ = 0 to 4 do
    printf $"{rand.Next 101,8:N0}"
printfn ""

// Generate and display 5 random integers from 50 to 100.
printfn "Five random integers between 50 and 100:"
for _ = 0 to 4 do
    printf $"{rand.Next(50, 101),8:N0}"
printfn ""

// Generate and display 5 random floating point values from 0 to 1.
printfn "Five Doubles."
for _ = 0 to 4 do
    printf $"{rand.NextDouble(),8:N3}"
printfn ""

// Generate and display 5 random floating point values from 0 to 5.
printfn "Five Doubles between 0 and 5."
for _ = 0 to 4 do
    printf $"{rand.NextDouble() * 5.0,8:N3}"

// The example displays output like the following:
//    Five random byte values:
//      194  185  239   54  116
//    Five random integer values:
//        507,353,531  1,509,532,693  2,125,074,958  1,409,512,757    652,767,128
//    Five random integers between 0 and 100:
//          16      78      94      79      52
//    Five random integers between 50 and 100:
//          56      66      96      60      65
//    Five Doubles.
//       0.943   0.108   0.744   0.563   0.415
//    Five Doubles between 0 and 5.
//       2.934   3.130   0.292   1.432   4.369
Module RandomExample2
    Public Sub Main()
        ' Instantiate random number generator using system-supplied value as seed.
        Dim rand As New Random()
        ' Generate and display 5 random byte (integer) values.
        Dim bytes(4) As Byte
        rand.NextBytes(bytes)
        Console.WriteLine("Five random byte values:")
        For Each byteValue As Byte In bytes
            Console.Write("{0, 5}", byteValue)
        Next
        Console.WriteLine()
        ' Generate and display 5 random integers.
        Console.WriteLine("Five random integer values:")
        For ctr As Integer = 0 To 4
            Console.Write("{0,15:N0}", rand.Next)
        Next
        Console.WriteLine()
        ' Generate and display 5 random integers between 0 and 100.'
        Console.WriteLine("Five random integers between 0 and 100:")
        For ctr As Integer = 0 To 4
            Console.Write("{0,8:N0}", rand.Next(101))
        Next
        Console.WriteLine()
        ' Generate and display 5 random integers from 50 to 100.
        Console.WriteLine("Five random integers between 50 and 100:")
        For ctr As Integer = 0 To 4
            Console.Write("{0,8:N0}", rand.Next(50, 101))
        Next
        Console.WriteLine()
        ' Generate and display 5 random floating point values from 0 to 1.
        Console.WriteLine("Five Doubles.")
        For ctr As Integer = 0 To 4
            Console.Write("{0,8:N3}", rand.NextDouble())
        Next
        Console.WriteLine()
        ' Generate and display 5 random floating point values from 0 to 5.
        Console.WriteLine("Five Doubles between 0 and 5.")
        For ctr As Integer = 0 To 4
            Console.Write("{0,8:N3}", rand.NextDouble() * 5)
        Next
    End Sub
End Module
' The example displays output like the following:
'    Five random byte values:
'      194  185  239   54  116
'    Five random integer values:
'        507,353,531  1,509,532,693  2,125,074,958  1,409,512,757    652,767,128
'    Five random integers between 0 and 100:
'          16      78      94      79      52
'    Five random integers between 50 and 100:
'          56      66      96      60      65
'    Five Doubles.
'       0.943   0.108   0.744   0.563   0.415
'    Five Doubles between 0 and 5.
'       2.934   3.130   0.292   1.432   4.369

以下範例生成一個隨機整數,作為索引,從陣列中檢索字串值。

Random rnd = new();
string[] malePetNames = [ "Rufus", "Bear", "Dakota", "Fido",
                        "Vanya", "Samuel", "Koani", "Volodya",
                        "Prince", "Yiska" ];
string[] femalePetNames = [ "Maggie", "Penny", "Saya", "Princess",
                          "Abby", "Laila", "Sadie", "Olivia",
                          "Starlight", "Talla" ];

// Generate random indexes for pet names.
int mIndex = rnd.Next(malePetNames.Length);
int fIndex = rnd.Next(femalePetNames.Length);

// Display the result.
Console.WriteLine("Suggested pet name of the day: ");
Console.WriteLine($"   For a male:     {malePetNames[mIndex]}");
Console.WriteLine($"   For a female:   {femalePetNames[fIndex]}");

// The example displays output similar to the following:
//       Suggested pet name of the day:
//          For a male:     Koani
//          For a female:   Maggie
let rnd = Random()

let malePetNames =
    [| "Rufus"; "Bear"; "Dakota"; "Fido";
        "Vanya"; "Samuel"; "Koani"; "Volodya";
        "Prince"; "Yiska" |]
let femalePetNames = 
    [| "Maggie"; "Penny"; "Saya"; "Princess";
        "Abby"; "Laila"; "Sadie"; "Olivia";
        "Starlight"; "Talla" |]

// Generate random indexes for pet names.
let mIndex = rnd.Next malePetNames.Length
let fIndex = rnd.Next femalePetNames.Length

// Display the result.
printfn "Suggested pet name of the day: "
printfn "   For a male:     %s" malePetNames.[mIndex]
printfn "   For a female:   %s" femalePetNames.[fIndex]

// The example displays output similar to the following:
//       Suggested pet name of the day:
//          For a male:     Koani
//          For a female:   Maggie
Module Example
   Public Sub Main()
      Dim rnd As New Random()
      Dim malePetNames() As String = { "Rufus", "Bear", "Dakota", "Fido", 
                                    "Vanya", "Samuel", "Koani", "Volodya", 
                                    "Prince", "Yiska" }
      Dim femalePetNames() As String = { "Maggie", "Penny", "Saya", "Princess", 
                                         "Abby", "Laila", "Sadie", "Olivia", 
                                         "Starlight", "Talla" }                                      
      
      ' Generate random indexes for pet names.
      Dim mIndex As Integer = rnd.Next(malePetNames.Length)
      Dim fIndex As Integer = rnd.Next(femalePetNames.Length)
      
      ' Display the result.
      Console.WriteLine("Suggested pet name of the day: ")
      Console.WriteLine("   For a male:     {0}", malePetNames(mIndex))
      Console.WriteLine("   For a female:   {0}", femalePetNames(fIndex))
   End Sub
End Module
' The example displays output similar to the following:
'       Suggested pet name of the day:
'          For a male:     Koani
'          For a female:   Maggie

備註

Note

若要產生密碼編譯安全隨機數,例如適合建立隨機密碼的隨機數位,請使用 類別中的 RandomNumberGenerator 其中一個靜態方法。

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

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

若要產生密碼編譯安全隨機數,例如適合建立隨機密碼的隨機數位,請使用 類別中的 System.Security.Cryptography.RandomNumberGenerator 其中一個靜態方法。

具現化隨機數產生器

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

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

預設的種子值由執行緒靜態偽隨機數產生器產生。

如果相同的種子用於不同的 Random 物件,它們會產生相同的隨機數序列。 這對於建立可處理隨機值的測試套件,或重新執行從隨機數位衍生其數據的遊戲很有用。

若要產生不同的隨機數序列,您可以讓種子值時間相依,藉此產生具有每個新 實例 Random的不同數列。 參數化 Random(Int32) 建構函式可以根據目前時間的刻度數來取得 Int32 值,而無 Random() 參數建構函式則使用系統時鐘來產生其種子值。

螺紋安全

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

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

using System;
using System.Threading;

public class Example18
{
    [ThreadStatic] static double s_previous;
    [ThreadStatic] static int s_perThreadCtr;
    [ThreadStatic] static double s_perThreadTotal;
    static CancellationTokenSource s_source;
    static CountdownEvent s_countdown;
    static object s_randLock, s_numericLock;
    static Random s_rand;
    double _totalValue = 0.0;
    int _totalCount = 0;

    public Example18()
    {
        s_rand = new Random();
        s_randLock = new object();
        s_numericLock = new object();
        s_countdown = new CountdownEvent(1);
        s_source = new CancellationTokenSource();
    }

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

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

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

        s_countdown.Signal();
        // Make sure all threads have finished.
        s_countdown.Wait();
        s_source.Dispose();

        Console.WriteLine($"\nTotal random numbers generated: {_totalCount:N0}");
        Console.WriteLine($"Total sum of all random numbers: {_totalValue:N2}");
        Console.WriteLine($"Random number mean: {_totalValue / _totalCount:N4}");
    }

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

        try
        {
            s_previous = 0.0;
            s_perThreadCtr = 0;
            s_perThreadTotal = 0.0;

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

                lock (s_randLock)
                {
                    result = s_rand.NextDouble();
                }
                // Check for corruption of Random instance.
                if ((result == s_previous) && result == 0)
                {
                    s_source.Cancel();
                }
                else
                {
                    s_previous = result;
                }
                s_perThreadCtr++;
                s_perThreadTotal += result;
            }

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

            // Update overall totals.
            lock (s_numericLock)
            {
                _totalCount += s_perThreadCtr;
                _totalValue += s_perThreadTotal;
            }
        }
        catch (OperationCanceledException e)
        {
            Console.WriteLine($"Corruption in Thread {Thread.CurrentThread.Name}");
        }
        finally
        {
            s_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 範例相同,不同之處在於它使用Task物件和Lambda 表達式,而不是物件。

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

public class Example19
{
    static object s_randLock, s_numericLock;
    static Random s_rand;
    static CancellationTokenSource s_source;
    double _totalValue = 0.0;
    int _totalCount = 0;

    public Example19()
    {
        s_rand = new Random();
        s_randLock = new object();
        s_numericLock = new object();
        s_source = new CancellationTokenSource();
    }

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

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

        for (int ctr = 0; ctr <= 10; ctr++)
        {
            CancellationToken token = s_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 (s_randLock)
                       {
                           result = s_rand.NextDouble();
                       }
                       // Check for corruption of Random instance.
                       if ((result == previous) && result == 0)
                       {
                           s_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 (s_numericLock)
                   {
                       _totalCount += taskCtr;
                       _totalValue += taskTotal;
                   }
               },
            token));
        }
        try
        {
            await Task.WhenAll(tasks.ToArray());
            Console.WriteLine($"\nTotal random numbers generated: {_totalCount:N0}");
            Console.WriteLine($"Total sum of all random numbers: {_totalValue:N2}");
            Console.WriteLine($"Random number mean: {_totalValue / _totalCount:N4}");
        }
        catch (AggregateException e)
        {
            foreach (Exception inner in e.InnerExceptions)
            {
                if (inner is TaskCanceledException canc)
                    Console.WriteLine("Task #{0} cancelled.", canc.Task.Id);
                else
                    Console.WriteLine("Exception: {0}", inner.GetType().Name);
            }
        }
        finally
        {
            s_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();
    byte[] bytes = new byte[20];
    rnd.NextBytes(bytes);
    for (int ctr = 1; ctr <= bytes.Length; ctr++)
    {
        Console.Write($"{bytes[ctr - 1],3}   ");
        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
    
  • 單一整數。 您可以選擇是否要透過呼叫 Int32.MaxValue 方法來獲取從 0 到最大值(Next() - 1)的整數,藉由呼叫 Next(Int32) 方法來獲取介於 0 和特定值之間的整數,或者藉由呼叫 Next(Int32, Int32) 方法來獲取在某個範圍內的整數。 在參數化多載中,指定的最大值是獨佔的;也就是說,產生的實際最大數目小於指定的值。

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

    Random rnd = new();
    for (int ctr = 0; ctr < 10; ctr++)
    {
        Console.Write($"{rnd.Next(-10, 11),3}   ");
    }
    
    // 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();
    for (int ctr = 0; ctr < 10; ctr++)
    {
        Console.Write($"{rnd.NextDouble(),-19:R}   ");
        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
    

Important

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 Example17
{
    public static void Main()
    {
        int seed = 100100;
        ShowRandomNumbers(seed);
        Console.WriteLine();

        PersistSeed(seed);

        DisplayNewRandomNumbers();
    }

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

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

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

        Random rnd = new(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 Example20
{
    public static void Main()
    {
        Console.WriteLine("Instantiating two random number generators...");
        Random rnd1 = new();
        Thread.Sleep(2000);
        Random rnd2 = new();

        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

擷取指定範圍內的整數

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

Random rnd = new();
for (int ctr = 1; ctr <= 15; ctr++)
{
    Console.Write($"{rnd.Next(-10, 11),3}    ");
    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 且minValue值為 10000 的 方法maxValue,如下列範例所示。

Random rnd = new();
for (int ctr = 1; ctr <= 50; ctr++)
{
    Console.Write($"{rnd.Next(1000, 10000),3}    ");
    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();
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();
double number;
int[] count = new int[10];

// Generate 20 million integer values between.
for (int ctr = 1; ctr <= 20000000; ctr++)
{
    number = rnd.NextDouble() * long.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 : long.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();
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 值的方法。 不過,您可以定義自己的類別或方法來執行此動作。 下列範例會使用單一方法BooleanGenerator定義 類別 NextBoolean。 類別 BooleanGenerator 會將 Random 物件儲存為私用變數。 NextBoolean 方法會呼叫 Random.Next(Int32, Int32) 方法,並將結果傳遞至 Convert.ToBoolean(Int32) 方法。 請注意,2 是用來指定隨機數上限的自變數。 由於這是獨佔值,因此方法呼叫會傳回 0 或 1。

using System;

public class Example2
{
    public static void Main()
    {
        // Instantiate the Boolean generator.
        BooleanGenerator boolGen = new();
        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 實例可以定義為 變數。 下列範例提供了一個實作。

Random rnd = new();

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();
long number;
int[] count = new int[10];

// Generate 20 million long integers.
for (int ctr = 1; ctr <= 20000000; ctr++)
{
    number = (long)(rnd.NextDouble() * long.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 : long.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 Example4
{
    public static void Main()
    {
        Random2 rnd = new();
        byte[] bytes = new byte[10000];
        int[] total = new int[101];
        rnd.NextBytes(bytes, 0, 101);

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

        // Display the results.
        for (int ctr = 0; ctr < total.Length; ctr++)
        {
            Console.Write($"{ctr,3}: {total[ctr],-3}");
            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# 中作為型別轉換,在 Visual Basic 中作為轉換)。

從陣列或集合中隨機擷取元素

隨機數通常做為索引,從數位或集合擷取值。 若要擷取隨機索引值,您可以呼叫 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();
int index = rnd.Next(0, cities.Length);
Console.WriteLine($"Today's city of the day: {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陣列,代表牌組中的所有卡片;另一個是具有與order陣列相同元素數量的本地deck陣列,並填入隨機生成的Double值。 接著會呼叫Array.Sort(Array, Array)方法,根據deck陣列中的order值來排序。

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}", FaceValue, 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 (object suit in Enum.GetValues(typeof(Suit)))
        {
            foreach (object faceValue in Enum.GetValues(typeof(FaceValue)))
            {
                Card card = new()
                {
                    Suit = (Suit)suit,
                    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 {ctr + 1} cards remaining on the deck.");
                return cardsDealt;
            }
        }
        return cardsDealt;
    }
}

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

    private static void ShowCards(Card[] cards)
    {
        foreach (Card 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 UniqueArrayExample
    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

給繼承者的注意事項

在 .NET Framework 2.0 及以後版本中,、Next()Next(Int32, Int32)、 和 方法的行為NextBytes(Byte[])已改變,這些方法不一定會呼叫該方法的Sample()衍生類別實作。 因此,從 Random 該目標 .NET Framework 2.0 及之後版本衍生出的類別也應該覆蓋這三個方法。

給呼叫者的注意事項

類別中 Random 隨機數產生器的實作並不保證在各主要版本的 .NET 中保持一致。 因此,你不應該假設同一個種子在不同版本的 .NET 中會出現相同的偽隨機序列。

建構函式

名稱 Description
Random()

使用預設的種子值初始化該類別的新實例 Random

Random(Int32)

使用指定的種子值初始化該類別的新 Random 實例。

屬性

名稱 Description
Shared

提供一個可同時從任何執行緒使用的執行緒安全 Random 實例。

方法

名稱 Description
Equals(Object)

判斷指定的物件是否等於目前的物件。

(繼承來源 Object)
GetHashCode()

做為預設哈希函式。

(繼承來源 Object)
GetHexString(Int32, Boolean)

建立一個充滿隨機十六進位字元的字串。

GetHexString(Span<Char>, Boolean)

用隨機的十六進位字元填滿緩衝區。

GetItems<T>(ReadOnlySpan<T>, Int32)

建立一個陣列,填充從所提供選項中隨機選取的項目。

GetItems<T>(ReadOnlySpan<T>, Span<T>)

將指定區間的元素填滿從所提供選項集中隨機選擇的項目。

GetItems<T>(T[], Int32)

建立一個陣列,填充從所提供選項中隨機選取的項目。

GetString(ReadOnlySpan<Char>, Int32)

建立一個由隨機從中 choices選取的字元填充的字串。

GetType()

取得目前實例的 Type

(繼承來源 Object)
MemberwiseClone()

建立目前 Object的淺層複本。

(繼承來源 Object)
Next()

回傳一個非負的隨機整數。

Next(Int32, Int32)

回傳一個在指定範圍內的隨機整數。

Next(Int32)

回傳一個小於指定最大值的非負隨機整數。

NextBinaryFloat<T>()

代表一種偽隨機數產生器,這是一種產生符合特定統計隨機性要求的數字序列的演算法。

NextBytes(Byte[])

用隨機數填滿指定位元組陣列的元素。

NextBytes(Span<Byte>)

用隨機數填補指定位元組區段的元素。

NextDouble()

回傳一個大於或等於 0.0 且小於 1.0 的隨機浮點數。

NextInt64()

回傳一個非負的隨機整數。

NextInt64(Int64, Int64)

回傳一個在指定範圍內的隨機整數。

NextInt64(Int64)

回傳一個小於指定最大值的非負隨機整數。

NextInteger<T>()

代表一種偽隨機數產生器,這是一種產生符合特定統計隨機性要求的數字序列的演算法。

NextInteger<T>(T, T)

代表一種偽隨機數產生器,這是一種產生符合特定統計隨機性要求的數字序列的演算法。

NextInteger<T>(T)

代表一種偽隨機數產生器,這是一種產生符合特定統計隨機性要求的數字序列的演算法。

NextSingle()

回傳一個大於或等於 0.0 且小於 1.0 的隨機浮點數。

Sample()

回傳一個介於 0.0 到 1.0 之間的隨機浮點數。

Shuffle<T>(Span<T>)

執行原地推移跨距。

Shuffle<T>(T[])

執行陣列的原位洗牌。

ToString()

傳回表示目前 物件的字串。

(繼承來源 Object)

適用於