System.Random – třída
Tento článek obsahuje doplňující poznámky k referenční dokumentaci pro toto rozhraní API.
Třída Random představuje pseudonáhodný generátor čísel, což je algoritmus, který vytváří sekvenci čísel, která splňují určité statistické požadavky na náhodnost.
Pseudonáhodná čísla jsou zvolena se stejnou pravděpodobností z konečné množiny čísel. Vybraná čísla nejsou zcela náhodná, protože k jejich výběru se používá matematický algoritmus, ale jsou dostatečně náhodná pro praktické účely. Implementace třídy je založena na upravené verzi Algoritmu generátoru náhodných Random čísel Donalda E. Knutha. Další informace naleznete v tématu D. E. Knuth. Umění počítačového programování, svazek 2: Seminumerické algoritmy. Addison-Wesley, Reading, MA, třetí vydání, 1997.
Chcete-li vygenerovat kryptograficky bezpečné náhodné číslo, například číslo vhodné pro vytvoření náhodného hesla, použijte jednu ze statických metod ve System.Security.Cryptography.RandomNumberGenerator třídě.
Vytvoření instance generátoru náhodných čísel
Vytvoření instance generátoru náhodných čísel poskytnutím počáteční hodnoty (počáteční hodnota pro algoritmus generování pseudonáhodných čísel) konstruktoru Random třídy. Počáteční hodnotu můžete zadat explicitně nebo implicitně:
- Konstruktor Random(Int32) používá explicitní počáteční hodnotu, kterou zadáte.
- Konstruktor Random() používá výchozí počáteční hodnotu. Toto je nejběžnější způsob vytvoření instance generátoru náhodných čísel.
V rozhraní .NET Framework je výchozí počáteční hodnota závislá na čase. V .NET Core je výchozí počáteční hodnota vytvořená generátorem pseudonáhodných čísel ve vláknech.
Pokud se stejné počáteční hodnoty používají pro samostatné Random objekty, vygenerují stejnou řadu náhodných čísel. To může být užitečné pro vytvoření sady testů, která zpracovává náhodné hodnoty, nebo pro přehrání her, které odvozují data z náhodných čísel. Mějte však na paměti, že objekty v procesech spuštěných v různých verzích rozhraní .NET Framework můžou vracet různé skupiny náhodných čísel i v případě, že Random se vytvoří instance se stejnými počátečními hodnotami.
Chcete-li vytvořit různé sekvence náhodných čísel, můžete nastavit, aby počáteční hodnota byla závislá na čase, čímž vytvoří jinou řadu s každou novou instancí Random. Parametrizovaný Random(Int32) konstruktor může vzít Int32 hodnotu na základě počtu záškrtů v aktuálním čase, zatímco konstruktor bez Random() parametrů používá systémové hodiny k vygenerování počáteční hodnoty. Nicméně pouze v rozhraní .NET Framework, protože hodiny mají konečné rozlišení, pomocí konstruktoru bez parametrů vytvořit různé Random objekty v těsné posloupnosti vytváří generátory náhodných čísel, které vytvářejí identické sekvence náhodných čísel. Následující příklad ukazuje, jak dva Random objekty, které jsou vytvořeny v těsné posloupnosti v aplikaci .NET Framework generují identickou řadu náhodných čísel. Ve většině systémů Random Windows budou objekty vytvořené v rozmezí 15 milisekund od sebe pravděpodobně mít stejné počáteční hodnoty.
byte[] bytes1 = new byte[100];
byte[] bytes2 = new byte[100];
Random rnd1 = new Random();
Random rnd2 = new Random();
rnd1.NextBytes(bytes1);
rnd2.NextBytes(bytes2);
Console.WriteLine("First Series:");
for (int ctr = bytes1.GetLowerBound(0);
ctr <= bytes1.GetUpperBound(0);
ctr++) {
Console.Write("{0, 5}", bytes1[ctr]);
if ((ctr + 1) % 10 == 0) Console.WriteLine();
}
Console.WriteLine();
Console.WriteLine("Second Series:");
for (int ctr = bytes2.GetLowerBound(0);
ctr <= bytes2.GetUpperBound(0);
ctr++) {
Console.Write("{0, 5}", bytes2[ctr]);
if ((ctr + 1) % 10 == 0) Console.WriteLine();
}
// The example displays output like the following:
// First Series:
// 97 129 149 54 22 208 120 105 68 177
// 113 214 30 172 74 218 116 230 89 18
// 12 112 130 105 116 180 190 200 187 120
// 7 198 233 158 58 51 50 170 98 23
// 21 1 113 74 146 245 34 255 96 24
// 232 255 23 9 167 240 255 44 194 98
// 18 175 173 204 169 171 236 127 114 23
// 167 202 132 65 253 11 254 56 214 127
// 145 191 104 163 143 7 174 224 247 73
// 52 6 231 255 5 101 83 165 160 231
//
// Second Series:
// 97 129 149 54 22 208 120 105 68 177
// 113 214 30 172 74 218 116 230 89 18
// 12 112 130 105 116 180 190 200 187 120
// 7 198 233 158 58 51 50 170 98 23
// 21 1 113 74 146 245 34 255 96 24
// 232 255 23 9 167 240 255 44 194 98
// 18 175 173 204 169 171 236 127 114 23
// 167 202 132 65 253 11 254 56 214 127
// 145 191 104 163 143 7 174 224 247 73
// 52 6 231 255 5 101 83 165 160 231
let bytes1 = Array.zeroCreate 100
let bytes2 = Array.zeroCreate 100
let rnd1 = Random()
let rnd2 = Random()
rnd1.NextBytes bytes1
rnd2.NextBytes bytes2
printfn "First Series"
for i = bytes1.GetLowerBound 0 to bytes1.GetUpperBound 0 do
printf "%5i" bytes1.[i]
if (i + 1) % 10 = 0 then printfn ""
printfn ""
printfn "Second Series"
for i = bytes2.GetLowerBound 0 to bytes2.GetUpperBound 0 do
printf "%5i" bytes2.[i]
if (i + 1) % 10 = 0 then printfn ""
// The example displays output like the following:
// First Series:
// 97 129 149 54 22 208 120 105 68 177
// 113 214 30 172 74 218 116 230 89 18
// 12 112 130 105 116 180 190 200 187 120
// 7 198 233 158 58 51 50 170 98 23
// 21 1 113 74 146 245 34 255 96 24
// 232 255 23 9 167 240 255 44 194 98
// 18 175 173 204 169 171 236 127 114 23
// 167 202 132 65 253 11 254 56 214 127
// 145 191 104 163 143 7 174 224 247 73
// 52 6 231 255 5 101 83 165 160 231
//
// Second Series:
// 97 129 149 54 22 208 120 105 68 177
// 113 214 30 172 74 218 116 230 89 18
// 12 112 130 105 116 180 190 200 187 120
// 7 198 233 158 58 51 50 170 98 23
// 21 1 113 74 146 245 34 255 96 24
// 232 255 23 9 167 240 255 44 194 98
// 18 175 173 204 169 171 236 127 114 23
// 167 202 132 65 253 11 254 56 214 127
// 145 191 104 163 143 7 174 224 247 73
// 52 6 231 255 5 101 83 165 160 231
Module modMain
Public Sub Main()
Dim bytes1(99), bytes2(99) As Byte
Dim rnd1 As New Random()
Dim rnd2 As New Random()
rnd1.NextBytes(bytes1)
rnd2.NextBytes(bytes2)
Console.WriteLine("First Series:")
For ctr As Integer = bytes1.GetLowerBound(0) to bytes1.GetUpperBound(0)
Console.Write("{0, 5}", bytes1(ctr))
If (ctr + 1) Mod 10 = 0 Then Console.WriteLine()
Next
Console.WriteLine()
Console.WriteLine("Second Series:")
For ctr As Integer = bytes2.GetLowerBound(0) to bytes2.GetUpperBound(0)
Console.Write("{0, 5}", bytes2(ctr))
If (ctr + 1) Mod 10 = 0 Then Console.WriteLine()
Next
End Sub
End Module
' The example displays output like the following:
' First Series:
' 97 129 149 54 22 208 120 105 68 177
' 113 214 30 172 74 218 116 230 89 18
' 12 112 130 105 116 180 190 200 187 120
' 7 198 233 158 58 51 50 170 98 23
' 21 1 113 74 146 245 34 255 96 24
' 232 255 23 9 167 240 255 44 194 98
' 18 175 173 204 169 171 236 127 114 23
' 167 202 132 65 253 11 254 56 214 127
' 145 191 104 163 143 7 174 224 247 73
' 52 6 231 255 5 101 83 165 160 231
'
' Second Series:
' 97 129 149 54 22 208 120 105 68 177
' 113 214 30 172 74 218 116 230 89 18
' 12 112 130 105 116 180 190 200 187 120
' 7 198 233 158 58 51 50 170 98 23
' 21 1 113 74 146 245 34 255 96 24
' 232 255 23 9 167 240 255 44 194 98
' 18 175 173 204 169 171 236 127 114 23
' 167 202 132 65 253 11 254 56 214 127
' 145 191 104 163 143 7 174 224 247 73
' 52 6 231 255 5 101 83 165 160 231
Chcete-li se tomuto problému vyhnout, vytvořte jeden Random objekt místo více objektů. Všimněte si, že třída Random
v .NET Core toto omezení nemá.
Vyhněte se více instancím
V rozhraní .NET Framework inicializace dvou generátorů náhodných čísel v těsné smyčce nebo v rychlém sledu vytvoří dva generátory náhodných čísel, které mohou vytvářet identické sekvence náhodných čísel. Ve většině případů to není záměr vývojáře a může vést k problémům s výkonem, protože vytvoření instance a inicializace generátoru náhodných čísel je relativně nákladný proces.
Abyste zlepšili výkon a vyhnuli se neúmyslnému vytváření samostatných generátorů náhodných čísel, které generují identické číselné sekvence, doporučujeme vytvořit jeden Random objekt, abyste vygenerovali mnoho náhodných čísel v průběhu času, místo abyste vytvářeli nové Random objekty pro generování jednoho náhodného čísla.
Random Třída ale není bezpečná pro přístup z více vláken. Pokud voláte Random metody z více vláken, postupujte podle pokynů probíraných v další části.
Bezpečnost vlákna
Místo vytváření instancí jednotlivých Random objektů doporučujeme vytvořit jednu Random instanci, která vygeneruje všechna náhodná čísla potřebná vaší aplikací. Random Objekty však nejsou bezpečné pro přístup z více vláken. Pokud vaše aplikace volá Random metody z více vláken, musíte použít synchronizační objekt, abyste zajistili, že k generátoru náhodných čísel najednou bude mít přístup pouze jedno vlákno. Pokud se ujistěte, že Random k objektu přistupujete způsobem bezpečným pro přístup z více vláken, volání metod, které vracejí náhodná čísla 0.
Následující příklad používá příkaz lock jazyka C#, funkci F# lock a příkaz Visual Basic SyncLock k zajištění přístupu k jednomu generátoru náhodných čísel pomocí 11 vláken bezpečným způsobem. Každé vlákno generuje 2 miliony náhodných čísel, spočítá počet vygenerovaných náhodných čísel a vypočítá jejich součet a po dokončení provádění aktualizuje součty pro všechna vlákna.
using System;
using System.Threading;
public class Example13
{
[ThreadStatic] static double previous = 0.0;
[ThreadStatic] static int perThreadCtr = 0;
[ThreadStatic] static double perThreadTotal = 0.0;
static CancellationTokenSource source;
static CountdownEvent countdown;
static Object randLock, numericLock;
static Random rand;
double totalValue = 0.0;
int totalCount = 0;
public Example13()
{
rand = new Random();
randLock = new Object();
numericLock = new Object();
countdown = new CountdownEvent(1);
source = new CancellationTokenSource();
}
public static void Main()
{
Example13 ex = new Example13();
Thread.CurrentThread.Name = "Main";
ex.Execute();
}
private void Execute()
{
CancellationToken token = source.Token;
for (int threads = 1; threads <= 10; threads++)
{
Thread newThread = new Thread(this.GetRandomNumbers);
newThread.Name = threads.ToString();
newThread.Start(token);
}
this.GetRandomNumbers(token);
countdown.Signal();
// Make sure all threads have finished.
countdown.Wait();
source.Dispose();
Console.WriteLine("\nTotal random numbers generated: {0:N0}", totalCount);
Console.WriteLine("Total sum of all random numbers: {0:N2}", totalValue);
Console.WriteLine("Random number mean: {0:N4}", totalValue / totalCount);
}
private void GetRandomNumbers(Object o)
{
CancellationToken token = (CancellationToken)o;
double result = 0.0;
countdown.AddCount(1);
try
{
for (int ctr = 0; ctr < 2000000; ctr++)
{
// Make sure there's no corruption of Random.
token.ThrowIfCancellationRequested();
lock (randLock)
{
result = rand.NextDouble();
}
// Check for corruption of Random instance.
if ((result == previous) && result == 0)
{
source.Cancel();
}
else
{
previous = result;
}
perThreadCtr++;
perThreadTotal += result;
}
Console.WriteLine("Thread {0} finished execution.",
Thread.CurrentThread.Name);
Console.WriteLine("Random numbers generated: {0:N0}", perThreadCtr);
Console.WriteLine("Sum of random numbers: {0:N2}", perThreadTotal);
Console.WriteLine("Random number mean: {0:N4}\n", perThreadTotal / perThreadCtr);
// Update overall totals.
lock (numericLock)
{
totalCount += perThreadCtr;
totalValue += perThreadTotal;
}
}
catch (OperationCanceledException e)
{
Console.WriteLine("Corruption in Thread {1}", e.GetType().Name, Thread.CurrentThread.Name);
}
finally
{
countdown.Signal();
}
}
}
// The example displays output like the following:
// Thread 6 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 1,000,491.05
// Random number mean: 0.5002
//
// Thread 10 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 999,329.64
// Random number mean: 0.4997
//
// Thread 4 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 1,000,166.89
// Random number mean: 0.5001
//
// Thread 8 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 999,628.37
// Random number mean: 0.4998
//
// Thread Main finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 999,920.89
// Random number mean: 0.5000
//
// Thread 3 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 999,370.45
// Random number mean: 0.4997
//
// Thread 7 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 999,330.92
// Random number mean: 0.4997
//
// Thread 9 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 1,000,172.79
// Random number mean: 0.5001
//
// Thread 5 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 1,000,079.43
// Random number mean: 0.5000
//
// Thread 1 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 999,817.91
// Random number mean: 0.4999
//
// Thread 2 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 999,930.63
// Random number mean: 0.5000
//
//
// Total random numbers generated: 22,000,000
// Total sum of all random numbers: 10,998,238.98
// Random number mean: 0.4999
open System
open System.Threading
type Example() =
[<ThreadStatic; DefaultValue>]
static val mutable private previous : float
[<ThreadStatic; DefaultValue>]
static val mutable private perThreadCtr : int
[<ThreadStatic; DefaultValue>]
static val mutable private perThreadTotal : float
static let source = new CancellationTokenSource()
static let countdown = new CountdownEvent(1)
static let randLock = obj ()
static let numericLock = obj ()
static let rand = Random()
let mutable totalValue = 0.0
let mutable totalCount = 0
member _.GetRandomNumbers(token: CancellationToken) =
let mutable result = 0.0
countdown.AddCount 1
try
try
for _ = 0 to 1999999 do
// Make sure there's no corruption of Random.
token.ThrowIfCancellationRequested()
lock randLock (fun () ->
result <- rand.NextDouble() )
// Check for corruption of Random instance.
if result = Example.previous && result = 0.0 then
source.Cancel()
else
Example.previous <- result
Example.perThreadCtr <- Example.perThreadCtr + 1
Example.perThreadTotal <- Example.perThreadTotal + result
// Update overall totals.
lock numericLock (fun () ->
// Show result.
printfn "Thread %s finished execution." Thread.CurrentThread.Name
printfn $"Random numbers generated: {Example.perThreadCtr:N0}"
printfn $"Sum of random numbers: {Example.perThreadTotal:N2}"
printfn $"Random number mean: {(Example.perThreadTotal / float Example.perThreadCtr):N4}\n"
// Update overall totals.
totalCount <- totalCount + Example.perThreadCtr
totalValue <- totalValue + Example.perThreadTotal)
with :? OperationCanceledException as e ->
printfn "Corruption in Thread %s %s" (e.GetType().Name) Thread.CurrentThread.Name
finally
countdown.Signal() |> ignore
member this.Execute() =
let token = source.Token
for i = 1 to 10 do
let newThread = Thread(fun () -> this.GetRandomNumbers token)
newThread.Name <- string i
newThread.Start()
this.GetRandomNumbers token
countdown.Signal() |> ignore
countdown.Wait()
source.Dispose()
printfn $"\nTotal random numbers generated: {totalCount:N0}"
printfn $"Total sum of all random numbers: {totalValue:N2}"
printfn $"Random number mean: {(totalValue / float totalCount):N4}"
let ex = Example()
Thread.CurrentThread.Name <- "Main"
ex.Execute()
// The example displays output like the following:
// Thread 6 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 1,000,491.05
// Random number mean: 0.5002
//
// Thread 10 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 999,329.64
// Random number mean: 0.4997
//
// Thread 4 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 1,000,166.89
// Random number mean: 0.5001
//
// Thread 8 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 999,628.37
// Random number mean: 0.4998
//
// Thread Main finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 999,920.89
// Random number mean: 0.5000
//
// Thread 3 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 999,370.45
// Random number mean: 0.4997
//
// Thread 7 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 999,330.92
// Random number mean: 0.4997
//
// Thread 9 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 1,000,172.79
// Random number mean: 0.5001
//
// Thread 5 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 1,000,079.43
// Random number mean: 0.5000
//
// Thread 1 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 999,817.91
// Random number mean: 0.4999
//
// Thread 2 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 999,930.63
// Random number mean: 0.5000
//
//
// Total random numbers generated: 22,000,000
// Total sum of all random numbers: 10,998,238.98
// Random number mean: 0.4999
Imports System.Threading
Module Example15
<ThreadStatic> Dim previous As Double = 0.0
<ThreadStatic> Dim perThreadCtr As Integer = 0
<ThreadStatic> Dim perThreadTotal As Double = 0.0
Dim source As New CancellationTokenSource()
Dim countdown As New CountdownEvent(1)
Dim randLock As New Object()
Dim numericLock As New Object()
Dim rand As New Random()
Dim totalValue As Double = 0.0
Dim totalCount As Integer = 0
Public Sub Main()
Thread.CurrentThread.Name = "Main"
Dim token As CancellationToken = source.Token
For threads As Integer = 1 To 10
Dim newThread As New Thread(AddressOf GetRandomNumbers)
newThread.Name = threads.ToString()
newThread.Start(token)
Next
GetRandomNumbers(token)
countdown.Signal()
' Make sure all threads have finished.
countdown.Wait()
Console.WriteLine()
Console.WriteLine("Total random numbers generated: {0:N0}", totalCount)
Console.WriteLine("Total sum of all random numbers: {0:N2}", totalValue)
Console.WriteLine("Random number mean: {0:N4}", totalValue / totalCount)
End Sub
Private Sub GetRandomNumbers(o As Object)
Dim token As CancellationToken = CType(o, CancellationToken)
Dim result As Double = 0.0
countdown.AddCount(1)
Try
For ctr As Integer = 1 To 2000000
' Make sure there's no corruption of Random.
token.ThrowIfCancellationRequested()
SyncLock randLock
result = rand.NextDouble()
End SyncLock
' Check for corruption of Random instance.
If result = previous AndAlso result = 0 Then
source.Cancel()
Else
previous = result
End If
perThreadCtr += 1
perThreadTotal += result
Next
Console.WriteLine("Thread {0} finished execution.",
Thread.CurrentThread.Name)
Console.WriteLine("Random numbers generated: {0:N0}", perThreadCtr)
Console.WriteLine("Sum of random numbers: {0:N2}", perThreadTotal)
Console.WriteLine("Random number mean: {0:N4}", perThreadTotal / perThreadCtr)
Console.WriteLine()
' Update overall totals.
SyncLock numericLock
totalCount += perThreadCtr
totalValue += perThreadTotal
End SyncLock
Catch e As OperationCanceledException
Console.WriteLine("Corruption in Thread {1}", e.GetType().Name, Thread.CurrentThread.Name)
Finally
countdown.Signal()
source.Dispose()
End Try
End Sub
End Module
' The example displays output like the following:
' Thread 6 finished execution.
' Random numbers generated: 2,000,000
' Sum of random numbers: 1,000,491.05
' Random number mean: 0.5002
'
' Thread 10 finished execution.
' Random numbers generated: 2,000,000
' Sum of random numbers: 999,329.64
' Random number mean: 0.4997
'
' Thread 4 finished execution.
' Random numbers generated: 2,000,000
' Sum of random numbers: 1,000,166.89
' Random number mean: 0.5001
'
' Thread 8 finished execution.
' Random numbers generated: 2,000,000
' Sum of random numbers: 999,628.37
' Random number mean: 0.4998
'
' Thread Main finished execution.
' Random numbers generated: 2,000,000
' Sum of random numbers: 999,920.89
' Random number mean: 0.5000
'
' Thread 3 finished execution.
' Random numbers generated: 2,000,000
' Sum of random numbers: 999,370.45
' Random number mean: 0.4997
'
' Thread 7 finished execution.
' Random numbers generated: 2,000,000
' Sum of random numbers: 999,330.92
' Random number mean: 0.4997
'
' Thread 9 finished execution.
' Random numbers generated: 2,000,000
' Sum of random numbers: 1,000,172.79
' Random number mean: 0.5001
'
' Thread 5 finished execution.
' Random numbers generated: 2,000,000
' Sum of random numbers: 1,000,079.43
' Random number mean: 0.5000
'
' Thread 1 finished execution.
' Random numbers generated: 2,000,000
' Sum of random numbers: 999,817.91
' Random number mean: 0.4999
'
' Thread 2 finished execution.
' Random numbers generated: 2,000,000
' Sum of random numbers: 999,930.63
' Random number mean: 0.5000
'
'
' Total random numbers generated: 22,000,000
' Total sum of all random numbers: 10,998,238.98
' Random number mean: 0.4999
Příklad zajišťuje bezpečnost vláken následujícími způsoby:
- Atribut ThreadStaticAttribute slouží k definování místních proměnných vlákna, které sledují celkový počet vygenerovaných náhodných čísel a jejich součet pro každé vlákno.
- Zámek (příkaz
lock
v jazyce C#,lock
funkce v jazyce F# aSyncLock
příkaz v jazyce Visual Basic) chrání přístup k proměnným pro celkový počet a součet všech náhodných čísel vygenerovaných ve všech vláknech. - Semaphore ( CountdownEvent objekt) se používá k zajištění, že hlavní vlákno blokuje, dokud všechny ostatní vlákna dokončit provádění.
- Příklad zkontroluje, zda generátor náhodných čísel byl poškozen určením, zda dvě po sobě jdoucí volání metod náhodného generování čísel vrací hodnotu 0. Pokud je zjištěno poškození, příklad používá CancellationTokenSource objekt k označení, že všechna vlákna by měla být zrušena.
- Před generováním každého náhodného čísla každé vlákno zkontroluje stav objektu CancellationToken . Pokud je požadováno zrušení, příklad volá metodu CancellationToken.ThrowIfCancellationRequested pro zrušení vlákna.
Následující příklad je shodný s prvním, s tím rozdílem, že používá Task objekt a výraz lambda místo Thread objektů.
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
public class Example15
{
static Object randLock, numericLock;
static Random rand;
static CancellationTokenSource source;
double totalValue = 0.0;
int totalCount = 0;
public Example15()
{
rand = new Random();
randLock = new Object();
numericLock = new Object();
source = new CancellationTokenSource();
}
public static async Task Main()
{
Example15 ex = new Example15();
Thread.CurrentThread.Name = "Main";
await ex.Execute();
}
private async Task Execute()
{
List<Task> tasks = new List<Task>();
for (int ctr = 0; ctr <= 10; ctr++)
{
CancellationToken token = source.Token;
int taskNo = ctr;
tasks.Add(Task.Run(() =>
{
double previous = 0.0;
int taskCtr = 0;
double taskTotal = 0.0;
double result = 0.0;
for (int n = 0; n < 2000000; n++)
{
// Make sure there's no corruption of Random.
token.ThrowIfCancellationRequested();
lock (randLock)
{
result = rand.NextDouble();
}
// Check for corruption of Random instance.
if ((result == previous) && result == 0)
{
source.Cancel();
}
else
{
previous = result;
}
taskCtr++;
taskTotal += result;
}
// Show result.
Console.WriteLine("Task {0} finished execution.", taskNo);
Console.WriteLine("Random numbers generated: {0:N0}", taskCtr);
Console.WriteLine("Sum of random numbers: {0:N2}", taskTotal);
Console.WriteLine("Random number mean: {0:N4}\n", taskTotal / taskCtr);
// Update overall totals.
lock (numericLock)
{
totalCount += taskCtr;
totalValue += taskTotal;
}
},
token));
}
try
{
await Task.WhenAll(tasks.ToArray());
Console.WriteLine("\nTotal random numbers generated: {0:N0}", totalCount);
Console.WriteLine("Total sum of all random numbers: {0:N2}", totalValue);
Console.WriteLine("Random number mean: {0:N4}", totalValue / totalCount);
}
catch (AggregateException e)
{
foreach (Exception inner in e.InnerExceptions)
{
TaskCanceledException canc = inner as TaskCanceledException;
if (canc != null)
Console.WriteLine("Task #{0} cancelled.", canc.Task.Id);
else
Console.WriteLine("Exception: {0}", inner.GetType().Name);
}
}
finally
{
source.Dispose();
}
}
}
// The example displays output like the following:
// Task 1 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 1,000,502.47
// Random number mean: 0.5003
//
// Task 0 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 1,000,445.63
// Random number mean: 0.5002
//
// Task 2 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 1,000,556.04
// Random number mean: 0.5003
//
// Task 3 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 1,000,178.87
// Random number mean: 0.5001
//
// Task 4 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 999,819.17
// Random number mean: 0.4999
//
// Task 5 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 1,000,190.58
// Random number mean: 0.5001
//
// Task 6 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 999,720.21
// Random number mean: 0.4999
//
// Task 7 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 999,000.96
// Random number mean: 0.4995
//
// Task 8 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 999,499.33
// Random number mean: 0.4997
//
// Task 9 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 1,000,193.25
// Random number mean: 0.5001
//
// Task 10 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 999,960.82
// Random number mean: 0.5000
//
//
// Total random numbers generated: 22,000,000
// Total sum of all random numbers: 11,000,067.33
// Random number mean: 0.5000
open System
open System.Threading
open System.Threading.Tasks
type Example() =
static let source = new CancellationTokenSource()
static let rand = Random()
static let randLock = obj ()
static let numericLock = obj ()
let mutable totalValue = 0.0
let mutable totalCount = 0
member _.Execute() =
use source = source // Dispose of the CancellationTokenSource when we're done with it.
let token = source.Token
let tasks =
[| for i = 0 to 10 do
Task.Run(
(fun () ->
let mutable previous = 0.0
let mutable taskCtr = 0
let mutable taskTotal = 0.0
let mutable result = 0.0
for _ = 1 to 2000000 do
// Make sure there's no corruption of Random.
token.ThrowIfCancellationRequested()
lock randLock (fun () -> result <- rand.NextDouble())
// Check for corruption of Random instance.
if result = previous && result = 0.0 then
source.Cancel()
else
previous <- result
taskCtr <- taskCtr + 1
taskTotal <- taskTotal + result
lock numericLock (fun () ->
// Show result.
printfn "Task %i finished execution." i
printfn $"Random numbers generated: {taskCtr:N0}"
printfn $"Sum of random numbers: {taskTotal:N2}"
printfn $"Random number mean: {(taskTotal / float taskCtr):N4}\n"
// Update overall totals.
totalCount <- totalCount + taskCtr
totalValue <- totalValue + taskTotal)),
token
) |]
try
// Run tasks with F# Async.
Task.WhenAll tasks
|> Async.AwaitTask
|> Async.RunSynchronously
printfn $"\nTotal random numbers generated: {totalCount:N0}"
printfn $"Total sum of all random numbers: {totalValue:N2}"
printfn $"Random number mean: {(totalValue / float totalCount):N4}"
with
| :? AggregateException as e ->
for inner in e.InnerExceptions do
match inner with
| :? TaskCanceledException as canc ->
if canc <> null then
printfn $"Task #{canc.Task.Id} cancelled"
else
printfn $"Exception: {inner.GetType().Name}"
| _ -> ()
let ex = Example()
Thread.CurrentThread.Name <- "Main"
ex.Execute()
// The example displays output like the following:
// Task 1 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 1,000,502.47
// Random number mean: 0.5003
//
// Task 0 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 1,000,445.63
// Random number mean: 0.5002
//
// Task 2 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 1,000,556.04
// Random number mean: 0.5003
//
// Task 3 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 1,000,178.87
// Random number mean: 0.5001
//
// Task 4 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 999,819.17
// Random number mean: 0.4999
//
// Task 5 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 1,000,190.58
// Random number mean: 0.5001
//
// Task 6 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 999,720.21
// Random number mean: 0.4999
//
// Task 7 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 999,000.96
// Random number mean: 0.4995
//
// Task 8 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 999,499.33
// Random number mean: 0.4997
//
// Task 9 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 1,000,193.25
// Random number mean: 0.5001
//
// Task 10 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 999,960.82
// Random number mean: 0.5000
//
//
// Total random numbers generated: 22,000,000
// Total sum of all random numbers: 11,000,067.33
// Random number mean: 0.5000
Imports System.Collections.Generic
Imports System.Threading
Imports System.Threading.Tasks
Module Example16
Dim source As New CancellationTokenSource()
Dim randLock As New Object()
Dim numericLock As New Object()
Dim rand As New Random()
Dim totalValue As Double = 0.0
Dim totalCount As Integer = 0
Public Sub Main()
Dim tasks As New List(Of Task)()
For ctr As Integer = 1 To 10
Dim token As CancellationToken = source.Token
Dim taskNo As Integer = ctr
tasks.Add(Task.Run(
Sub()
Dim previous As Double = 0.0
Dim taskCtr As Integer = 0
Dim taskTotal As Double = 0.0
Dim result As Double = 0.0
For n As Integer = 1 To 2000000
' Make sure there's no corruption of Random.
token.ThrowIfCancellationRequested()
SyncLock randLock
result = rand.NextDouble()
End SyncLock
' Check for corruption of Random instance.
If result = previous AndAlso result = 0 Then
source.Cancel()
Else
previous = result
End If
taskCtr += 1
taskTotal += result
Next
' Show result.
Console.WriteLine("Task {0} finished execution.", taskNo)
Console.WriteLine("Random numbers generated: {0:N0}", taskCtr)
Console.WriteLine("Sum of random numbers: {0:N2}", taskTotal)
Console.WriteLine("Random number mean: {0:N4}", taskTotal / taskCtr)
Console.WriteLine()
' Update overall totals.
SyncLock numericLock
totalCount += taskCtr
totalValue += taskTotal
End SyncLock
End Sub, token))
Next
Try
Task.WaitAll(tasks.ToArray())
Console.WriteLine()
Console.WriteLine("Total random numbers generated: {0:N0}", totalCount)
Console.WriteLine("Total sum of all random numbers: {0:N2}", totalValue)
Console.WriteLine("Random number mean: {0:N4}", totalValue / totalCount)
Catch e As AggregateException
For Each inner As Exception In e.InnerExceptions
Dim canc As TaskCanceledException = TryCast(inner, TaskCanceledException)
If canc IsNot Nothing Then
Console.WriteLine("Task #{0} cancelled.", canc.Task.Id)
Else
Console.WriteLine("Exception: {0}", inner.GetType().Name)
End If
Next
Finally
source.Dispose()
End Try
End Sub
End Module
' The example displays output like the following:
' Task 1 finished execution.
' Random numbers generated: 2,000,000
' Sum of random numbers: 1,000,502.47
' Random number mean: 0.5003
'
' Task 0 finished execution.
' Random numbers generated: 2,000,000
' Sum of random numbers: 1,000,445.63
' Random number mean: 0.5002
'
' Task 2 finished execution.
' Random numbers generated: 2,000,000
' Sum of random numbers: 1,000,556.04
' Random number mean: 0.5003
'
' Task 3 finished execution.
' Random numbers generated: 2,000,000
' Sum of random numbers: 1,000,178.87
' Random number mean: 0.5001
'
' Task 4 finished execution.
' Random numbers generated: 2,000,000
' Sum of random numbers: 999,819.17
' Random number mean: 0.4999
'
' Task 5 finished execution.
' Random numbers generated: 2,000,000
' Sum of random numbers: 1,000,190.58
' Random number mean: 0.5001
'
' Task 6 finished execution.
' Random numbers generated: 2,000,000
' Sum of random numbers: 999,720.21
' Random number mean: 0.4999
'
' Task 7 finished execution.
' Random numbers generated: 2,000,000
' Sum of random numbers: 999,000.96
' Random number mean: 0.4995
'
' Task 8 finished execution.
' Random numbers generated: 2,000,000
' Sum of random numbers: 999,499.33
' Random number mean: 0.4997
'
' Task 9 finished execution.
' Random numbers generated: 2,000,000
' Sum of random numbers: 1,000,193.25
' Random number mean: 0.5001
'
' Task 10 finished execution.
' Random numbers generated: 2,000,000
' Sum of random numbers: 999,960.82
' Random number mean: 0.5000
'
'
' Total random numbers generated: 22,000,000
' Total sum of all random numbers: 11,000,067.33
' Random number mean: 0.5000
Liší se od prvního příkladu následujícími způsoby:
- Proměnné pro sledování počtu vygenerovaných náhodných čísel a jejich součet v každém úkolu jsou pro úkol místní, takže atribut není potřeba používat ThreadStaticAttribute .
- Statická Task.WaitAll metoda se používá k zajištění, že se hlavní vlákno nedokončí před dokončením všech úloh. Objekt není potřeba CountdownEvent .
- Výjimka, která je výsledkem zrušení úkolu, se v Task.WaitAll metodě zobrazí. V předchozím příkladu se zpracovává jednotlivými vlákny.
Generování různých typů náhodných čísel
Generátor náhodných čísel poskytuje metody, které umožňují vygenerovat následující typy náhodných čísel:
Byte Řada hodnot. Počet bajtů hodnot určíte předáním pole inicializovaného na počet prvků, které má metoda vrátit do NextBytes metody. Následující příklad vygeneruje 20 bajtů.
Random rnd = new Random(); Byte[] bytes = new Byte[20]; rnd.NextBytes(bytes); for (int ctr = 1; ctr <= bytes.Length; ctr++) { Console.Write("{0,3} ", bytes[ctr - 1]); if (ctr % 10 == 0) Console.WriteLine(); } // The example displays output like the following: // 141 48 189 66 134 212 211 71 161 56 // 181 166 220 133 9 252 222 57 62 62
let rnd = Random() let bytes = Array.zeroCreate 20 rnd.NextBytes bytes for i = 1 to bytes.Length do printf "%3i " bytes.[i - 1] if (i % 10 = 0) then printfn "" // The example displays output like the following: // 141 48 189 66 134 212 211 71 161 56 // 181 166 220 133 9 252 222 57 62 62
Module Example9 Public Sub Main() Dim rnd As New Random() Dim bytes(19) As Byte rnd.NextBytes(bytes) For ctr As Integer = 1 To bytes.Length Console.Write("{0,3} ", bytes(ctr - 1)) If ctr Mod 10 = 0 Then Console.WriteLine() Next End Sub End Module ' The example displays output like the following: ' 141 48 189 66 134 212 211 71 161 56 ' 181 166 220 133 9 252 222 57 62 62
Jedno celé číslo. Můžete zvolit, zda chcete celé číslo od 0 do maximální hodnoty (Int32.MaxValue - 1) voláním Next() metody, celého čísla v rozmezí od 0 do konkrétní hodnoty voláním Next(Int32) metody, nebo celé číslo v rozsahu hodnot voláním Next(Int32, Int32) metody. V parametrizovaných přetíženích je zadaná maximální hodnota výhradní; to znamená, že skutečné maximální vygenerované číslo je jedna menší než zadaná hodnota.
Následující příklad volá metodu Next(Int32, Int32) pro vygenerování 10 náhodných čísel mezi -10 a 10. Všimněte si, že druhý argument metody určuje výhradní horní mez rozsahu náhodných hodnot vrácených metodou. Jinými slovy, největší celé číslo, které metoda může vrátit, je jedna menší než tato hodnota.
Random rnd = new Random(); for (int ctr = 0; ctr < 10; ctr++) { Console.Write("{0,3} ", rnd.Next(-10, 11)); } // The example displays output like the following: // 2 9 -3 2 4 -7 -3 -8 -8 5
let rnd = Random() for i = 0 to 9 do printf "%3i " (rnd.Next(-10, 11)) // The example displays output like the following: // 2 9 -3 2 4 -7 -3 -8 -8 5
Module Example11 Public Sub Main() Dim rnd As New Random() For ctr As Integer = 0 To 9 Console.Write("{0,3} ", rnd.Next(-10, 11)) Next End Sub End Module ' The example displays output like the following: ' 2 9 -3 2 4 -7 -3 -8 -8 5
Jedna hodnota s plovoucí desetinou čárkou od 0,0 do menší než 1,0 voláním NextDouble metody. Výhradní horní mez náhodného čísla vráceného metodou je 1, takže jeho skutečná horní mez je 0,9999999999999998. Následující příklad vygeneruje 10 náhodných čísel s plovoucí desetinou čárkou.
Random rnd = new Random(); for (int ctr = 0; ctr < 10; ctr++) { Console.Write("{0,-19:R} ", rnd.NextDouble()); if ((ctr + 1) % 3 == 0) Console.WriteLine(); } // The example displays output like the following: // 0.7911680553998649 0.0903414949264105 0.79776258291572455 // 0.615568345233597 0.652644504165577 0.84023809378977776 // 0.099662564741290441 0.91341467383942321 0.96018602045261581 // 0.74772306473354022
let rnd = Random() for i = 0 to 9 do printf $"{rnd.NextDouble(),-19:R} " if (i + 1) % 3 = 0 then printfn "" // The example displays output like the following: // 0.7911680553998649 0.0903414949264105 0.79776258291572455 // 0.615568345233597 0.652644504165577 0.84023809378977776 // 0.099662564741290441 0.91341467383942321 0.96018602045261581 // 0.74772306473354022
Module Example10 Public Sub Main() Dim rnd As New Random() For ctr As Integer = 0 To 9 Console.Write("{0,-19:R} ", rnd.NextDouble()) If (ctr + 1) Mod 3 = 0 Then Console.WriteLine() Next End Sub End Module ' The example displays output like the following: ' 0.7911680553998649 0.0903414949264105 0.79776258291572455 ' 0.615568345233597 0.652644504165577 0.84023809378977776 ' 0.099662564741290441 0.91341467383942321 0.96018602045261581 ' 0.74772306473354022
Důležité
Metoda Next(Int32, Int32) umožňuje zadat rozsah vráceného náhodného čísla. maxValue
Parametr, který určuje vrácené číslo horního rozsahu, je však exkluzivní, nikoli inkluzivní hodnota. To znamená, že volání Next(0, 100)
metody vrátí hodnotu mezi 0 a 99, a ne mezi 0 a 100.
Třídu můžete použít Random také pro úlohy, jako je generování náhodných logických hodnot, generování náhodných hodnot s plovoucí desetinou čárkou v zadané oblasti, generování náhodných 64bitových celých čísel a načtení jedinečného prvku z pole nebo kolekce.
Nahrazení vlastního algoritmu
Vlastní generátor náhodných čísel můžete implementovat tak, že zdědíte třídu Random a zadáte algoritmus generování náhodných čísel. Pokud chcete zadat vlastní algoritmus, musíte přepsat metodu Sample , která implementuje algoritmus generování náhodných čísel. Měli byste také přepsat Next(), Next(Int32, Int32)a NextBytes metody, aby se zajistilo, že volají vaši přepsánou Sample metodu. Nemusíte přepisovat Next(Int32) metody a NextDouble metody.
Příklad, který je odvozen od Random třídy a upravuje výchozí pseudonáhodný generátor čísel, naleznete na Sample referenční stránce.
Načtení stejné posloupnosti náhodných hodnot
Někdy chcete vygenerovat stejnou sekvenci náhodných čísel ve scénářích softwarového testu a ve hře. Testování se stejnou sekvencí náhodných čísel umožňuje detekovat regrese a potvrdit opravy chyb. Pomocí stejné posloupnosti náhodných čísel ve hrách můžete přehrávat předchozí hry.
Stejnou sekvenci náhodných čísel můžete vygenerovat zadáním stejné počáteční hodnoty konstruktoru Random(Int32) . Počáteční hodnota poskytuje počáteční hodnotu pro pseudonáhodný algoritmus generování čísel. Následující příklad používá 100100 jako libovolnou počáteční hodnotu k vytvoření instance objektu Random , zobrazí 20 náhodných hodnot s plovoucí deseti čárkou a zachová počáteční hodnotu. Potom obnoví počáteční hodnotu, vytvoří instanci nového generátoru náhodných čísel a zobrazí stejné 20 náhodných hodnot s plovoucí deseti čárkou. Všimněte si, že v příkladu mohou vzniknout různé sekvence náhodných čísel, pokud se spustí v různých verzích rozhraní .NET.
using System;
using System.IO;
public class Example12
{
public static void Main()
{
int seed = 100100;
ShowRandomNumbers(seed);
Console.WriteLine();
PersistSeed(seed);
DisplayNewRandomNumbers();
}
private static void ShowRandomNumbers(int seed)
{
Random rnd = new Random(seed);
for (int ctr = 0; ctr <= 20; ctr++)
Console.WriteLine(rnd.NextDouble());
}
private static void PersistSeed(int seed)
{
FileStream fs = new FileStream(@".\seed.dat", FileMode.Create);
BinaryWriter bin = new BinaryWriter(fs);
bin.Write(seed);
bin.Close();
}
private static void DisplayNewRandomNumbers()
{
FileStream fs = new FileStream(@".\seed.dat", FileMode.Open);
BinaryReader bin = new BinaryReader(fs);
int seed = bin.ReadInt32();
bin.Close();
Random rnd = new Random(seed);
for (int ctr = 0; ctr <= 20; ctr++)
Console.WriteLine(rnd.NextDouble());
}
}
// The example displays output like the following:
// 0.500193602172748
// 0.0209461245783354
// 0.465869495396442
// 0.195512794514891
// 0.928583675496552
// 0.729333720509584
// 0.381455668891527
// 0.0508996467343064
// 0.019261200921266
// 0.258578445417145
// 0.0177532266908107
// 0.983277184415272
// 0.483650274334313
// 0.0219647376900375
// 0.165910115077118
// 0.572085966622497
// 0.805291457942357
// 0.927985211335116
// 0.4228545699375
// 0.523320379910674
// 0.157783938645285
//
// 0.500193602172748
// 0.0209461245783354
// 0.465869495396442
// 0.195512794514891
// 0.928583675496552
// 0.729333720509584
// 0.381455668891527
// 0.0508996467343064
// 0.019261200921266
// 0.258578445417145
// 0.0177532266908107
// 0.983277184415272
// 0.483650274334313
// 0.0219647376900375
// 0.165910115077118
// 0.572085966622497
// 0.805291457942357
// 0.927985211335116
// 0.4228545699375
// 0.523320379910674
// 0.157783938645285
open System
open System.IO
let showRandomNumbers seed =
let rnd = Random seed
for _ = 0 to 20 do
printfn $"{rnd.NextDouble()}"
let persistSeed (seed: int) =
use bin = new BinaryWriter(new FileStream(@".\seed.dat", FileMode.Create))
bin.Write seed
let displayNewRandomNumbers () =
use bin = new BinaryReader(new FileStream(@".\seed.dat", FileMode.Open))
let seed = bin.ReadInt32()
let rnd = Random seed
for _ = 0 to 20 do
printfn $"{rnd.NextDouble()}"
let seed = 100100
showRandomNumbers seed
printfn ""
persistSeed seed
displayNewRandomNumbers ()
// The example displays output like the following:
// 0.500193602172748
// 0.0209461245783354
// 0.465869495396442
// 0.195512794514891
// 0.928583675496552
// 0.729333720509584
// 0.381455668891527
// 0.0508996467343064
// 0.019261200921266
// 0.258578445417145
// 0.0177532266908107
// 0.983277184415272
// 0.483650274334313
// 0.0219647376900375
// 0.165910115077118
// 0.572085966622497
// 0.805291457942357
// 0.927985211335116
// 0.4228545699375
// 0.523320379910674
// 0.157783938645285
//
// 0.500193602172748
// 0.0209461245783354
// 0.465869495396442
// 0.195512794514891
// 0.928583675496552
// 0.729333720509584
// 0.381455668891527
// 0.0508996467343064
// 0.019261200921266
// 0.258578445417145
// 0.0177532266908107
// 0.983277184415272
// 0.483650274334313
// 0.0219647376900375
// 0.165910115077118
// 0.572085966622497
// 0.805291457942357
// 0.927985211335116
// 0.4228545699375
// 0.523320379910674
// 0.157783938645285
Imports System.IO
Module Example14
Public Sub Main()
Dim seed As Integer = 100100
ShowRandomNumbers(seed)
Console.WriteLine()
PersistSeed(seed)
DisplayNewRandomNumbers()
End Sub
Private Sub ShowRandomNumbers(seed As Integer)
Dim rnd As New Random(seed)
For ctr As Integer = 0 To 20
Console.WriteLine(rnd.NextDouble())
Next
End Sub
Private Sub PersistSeed(seed As Integer)
Dim fs As New FileStream(".\seed.dat", FileMode.Create)
Dim bin As New BinaryWriter(fs)
bin.Write(seed)
bin.Close()
End Sub
Private Sub DisplayNewRandomNumbers()
Dim fs As New FileStream(".\seed.dat", FileMode.Open)
Dim bin As New BinaryReader(fs)
Dim seed As Integer = bin.ReadInt32()
bin.Close()
Dim rnd As New Random(seed)
For ctr As Integer = 0 To 20
Console.WriteLine(rnd.NextDouble())
Next
End Sub
End Module
' The example displays output like the following:
' 0.500193602172748
' 0.0209461245783354
' 0.465869495396442
' 0.195512794514891
' 0.928583675496552
' 0.729333720509584
' 0.381455668891527
' 0.0508996467343064
' 0.019261200921266
' 0.258578445417145
' 0.0177532266908107
' 0.983277184415272
' 0.483650274334313
' 0.0219647376900375
' 0.165910115077118
' 0.572085966622497
' 0.805291457942357
' 0.927985211335116
' 0.4228545699375
' 0.523320379910674
' 0.157783938645285
'
' 0.500193602172748
' 0.0209461245783354
' 0.465869495396442
' 0.195512794514891
' 0.928583675496552
' 0.729333720509584
' 0.381455668891527
' 0.0508996467343064
' 0.019261200921266
' 0.258578445417145
' 0.0177532266908107
' 0.983277184415272
' 0.483650274334313
' 0.0219647376900375
' 0.165910115077118
' 0.572085966622497
' 0.805291457942357
' 0.927985211335116
' 0.4228545699375
' 0.523320379910674
' 0.157783938645285
Načtení jedinečných sekvencí náhodných čísel
Poskytnutí různých počátečních hodnot instancím třídy způsobí, že každý generátor náhodných Random čísel vytvoří jinou sekvenci hodnot. Počáteční hodnotu můžete zadat buď explicitně voláním konstruktoru Random(Int32) , nebo implicitně voláním konstruktoru Random() . Většina vývojářů volá konstruktor bez parametrů, který používá systémové hodiny. Následující příklad používá tento přístup k vytvoření instance dvou Random instancí. Každá instance zobrazí řadu 10 náhodných celých čísel.
using System;
using System.Threading;
public class Example16
{
public static void Main()
{
Console.WriteLine("Instantiating two random number generators...");
Random rnd1 = new Random();
Thread.Sleep(2000);
Random rnd2 = new Random();
Console.WriteLine("\nThe first random number generator:");
for (int ctr = 1; ctr <= 10; ctr++)
Console.WriteLine(" {0}", rnd1.Next());
Console.WriteLine("\nThe second random number generator:");
for (int ctr = 1; ctr <= 10; ctr++)
Console.WriteLine(" {0}", rnd2.Next());
}
}
// The example displays output like the following:
// Instantiating two random number generators...
//
// The first random number generator:
// 643164361
// 1606571630
// 1725607587
// 2138048432
// 496874898
// 1969147632
// 2034533749
// 1840964542
// 412380298
// 47518930
//
// The second random number generator:
// 1251659083
// 1514185439
// 1465798544
// 517841554
// 1821920222
// 195154223
// 1538948391
// 1548375095
// 546062716
// 897797880
open System
open System.Threading
printfn "Instantiating two random number generators..."
let rnd1 = Random()
Thread.Sleep 2000
let rnd2 = Random()
printfn "\nThe first random number generator:"
for _ = 1 to 10 do
printfn $" {rnd1.Next()}"
printfn "\nThe second random number generator:"
for _ = 1 to 10 do
printfn $" {rnd2.Next()}"
// The example displays output like the following:
// Instantiating two random number generators...
//
// The first random number generator:
// 643164361
// 1606571630
// 1725607587
// 2138048432
// 496874898
// 1969147632
// 2034533749
// 1840964542
// 412380298
// 47518930
//
// The second random number generator:
// 1251659083
// 1514185439
// 1465798544
// 517841554
// 1821920222
// 195154223
// 1538948391
// 1548375095
// 546062716
// 897797880
Imports System.Threading
Module Example17
Public Sub Main()
Console.WriteLine("Instantiating two random number generators...")
Dim rnd1 As New Random()
Thread.Sleep(2000)
Dim rnd2 As New Random()
Console.WriteLine()
Console.WriteLine("The first random number generator:")
For ctr As Integer = 1 To 10
Console.WriteLine(" {0}", rnd1.Next())
Next
Console.WriteLine()
Console.WriteLine("The second random number generator:")
For ctr As Integer = 1 To 10
Console.WriteLine(" {0}", rnd2.Next())
Next
End Sub
End Module
' The example displays output like the following:
' Instantiating two random number generators...
'
' The first random number generator:
' 643164361
' 1606571630
' 1725607587
' 2138048432
' 496874898
' 1969147632
' 2034533749
' 1840964542
' 412380298
' 47518930
'
' The second random number generator:
' 1251659083
' 1514185439
' 1465798544
' 517841554
' 1821920222
' 195154223
' 1538948391
' 1548375095
' 546062716
' 897797880
Kvůli konečnému rozlišení ale systémové hodiny nezjistí časové rozdíly, které jsou menší než přibližně 15 milisekund. Proto pokud váš kód volá Random() přetížení rozhraní .NET Framework k vytvoření instance dvou Random objektů po sobě, můžete neúmyslně poskytnout objekty s identickými počátečními hodnotami. (Třída Random v .NET Core toto omezení nemá.) Pokud to chcete vidět v předchozím příkladu, okomentujte Thread.Sleep volání metody a zkompilujte a spusťte příklad znovu.
Pokud tomu chcete zabránit, doporučujeme vytvořit instanci jednoho Random objektu, nikoli více objektů. Vzhledem k tomu, že Random není bezpečné vlákno, musíte použít některé synchronizační zařízení, pokud přistupujete k Random instanci z více vláken. Další informace najdete v části Zabezpečení vlákna. Případně můžete použít mechanismus zpoždění, například metodu Sleep použitou v předchozím příkladu, abyste zajistili, že instance dojde k více než 15 milisekundám.
Načtení celých čísel v zadaném rozsahu
Celá čísla v zadaném rozsahu můžete načíst voláním Next(Int32, Int32) metody, která umožňuje zadat dolní i horní mez čísel, která chcete vrátit generátor náhodných čísel. Horní mez je exkluzivní, nikoli inkluzivní hodnota. To znamená, že není zahrnuta do rozsahu hodnot vrácených metodou. Následující příklad používá tuto metodu ke generování náhodných celých čísel mezi -10 a 10. Všimněte si, že určuje 11, což je jedna větší než požadovaná hodnota, jako hodnota maxValue
argumentu volání metody.
Random rnd = new Random();
for (int ctr = 1; ctr <= 15; ctr++)
{
Console.Write("{0,3} ", rnd.Next(-10, 11));
if (ctr % 5 == 0) Console.WriteLine();
}
// The example displays output like the following:
// -2 -5 -1 -2 10
// -3 6 -4 -8 3
// -7 10 5 -2 4
let rnd = Random()
for i = 1 to 15 do
printf "%3i " (rnd.Next(-10, 11))
if i % 5 = 0 then printfn ""
// The example displays output like the following:
// -2 -5 -1 -2 10
// -3 6 -4 -8 3
// -7 10 5 -2 4
Module Example12
Public Sub Main()
Dim rnd As New Random()
For ctr As Integer = 1 To 15
Console.Write("{0,3} ", rnd.Next(-10, 11))
If ctr Mod 5 = 0 Then Console.WriteLine()
Next
End Sub
End Module
' The example displays output like the following:
' -2 -5 -1 -2 10
' -3 6 -4 -8 3
' -7 10 5 -2 4
Načtení celých čísel se zadaným počtem číslic
Metodu Next(Int32, Int32) můžete volat k načtení čísel se zadaným počtem číslic. Pokud chcete například načíst čísla se čtyřmi číslicemi (to znamená čísla v rozsahu od 1000 do 9999), zavoláte Next(Int32, Int32) metodu s minValue
hodnotou 1 000 a maxValue
hodnotou 1 0000, jak ukazuje následující příklad.
Random rnd = new Random();
for (int ctr = 1; ctr <= 50; ctr++)
{
Console.Write("{0,3} ", rnd.Next(1000, 10000));
if (ctr % 10 == 0) Console.WriteLine();
}
// The example displays output like the following:
// 9570 8979 5770 1606 3818 4735 8495 7196 7070 2313
// 5279 6577 5104 5734 4227 3373 7376 6007 8193 5540
// 7558 3934 3819 7392 1113 7191 6947 4963 9179 7907
// 3391 6667 7269 1838 7317 1981 5154 7377 3297 5320
// 9869 8694 2684 4949 2999 3019 2357 5211 9604 2593
let rnd = Random()
for i = 1 to 50 do
printf "%3i " (rnd.Next(1000, 10000))
if i % 10 = 0 then printfn ""
// The example displays output like the following:
// 9570 8979 5770 1606 3818 4735 8495 7196 7070 2313
// 5279 6577 5104 5734 4227 3373 7376 6007 8193 5540
// 7558 3934 3819 7392 1113 7191 6947 4963 9179 7907
// 3391 6667 7269 1838 7317 1981 5154 7377 3297 5320
// 9869 8694 2684 4949 2999 3019 2357 5211 9604 2593
Module Example13
Public Sub Main()
Dim rnd As New Random()
For ctr As Integer = 1 To 50
Console.Write("{0,3} ", rnd.Next(1000, 10000))
If ctr Mod 10 = 0 Then Console.WriteLine()
Next
End Sub
End Module
' The example displays output like the following:
' 9570 8979 5770 1606 3818 4735 8495 7196 7070 2313
' 5279 6577 5104 5734 4227 3373 7376 6007 8193 5540
' 7558 3934 3819 7392 1113 7191 6947 4963 9179 7907
' 3391 6667 7269 1838 7317 1981 5154 7377 3297 5320
' 9869 8694 2684 4949 2999 3019 2357 5211 9604 2593
Načtení hodnot s plovoucí desetinou čárkou v zadaném rozsahu
Metoda NextDouble vrátí náhodné hodnoty s plovoucí desetinou čárkou, které jsou v rozsahu od 0 do menší než 1. Často ale budete chtít vygenerovat náhodné hodnoty v některém jiném rozsahu.
Pokud je interval mezi minimální a maximální požadovanou hodnotou 1, můžete přidat rozdíl mezi požadovaným počátečním intervalem a 0 k číslu vrácené metodou NextDouble . Následující příklad vygeneruje 10 náhodných čísel mezi -1 a 0.
Random rnd = new Random();
for (int ctr = 1; ctr <= 10; ctr++)
Console.WriteLine(rnd.NextDouble() - 1);
// The example displays output like the following:
// -0.930412760437658
// -0.164699016215605
// -0.9851692803135
// -0.43468508843085
// -0.177202483255976
// -0.776813320245972
// -0.0713201854710096
// -0.0912875561468711
// -0.540621722368813
// -0.232211863730201
let rnd = Random()
for _ = 1 to 10 do
printfn "%O" (rnd.NextDouble() - 1.0)
// The example displays output like the following:
// -0.930412760437658
// -0.164699016215605
// -0.9851692803135
// -0.43468508843085
// -0.177202483255976
// -0.776813320245972
// -0.0713201854710096
// -0.0912875561468711
// -0.540621722368813
// -0.232211863730201
Module Example6
Public Sub Main()
Dim rnd As New Random()
For ctr As Integer = 1 To 10
Console.WriteLine(rnd.NextDouble() - 1)
Next
End Sub
End Module
' The example displays output like the following:
' -0.930412760437658
' -0.164699016215605
' -0.9851692803135
' -0.43468508843085
' -0.177202483255976
' -0.776813320245972
' -0.0713201854710096
' -0.0912875561468711
' -0.540621722368813
' -0.232211863730201
Chcete-li generovat náhodná čísla s plovoucí desetinou čárkou, jejichž dolní mez je 0, ale horní mez je větší než 1 (nebo v případě záporných čísel, jejichž dolní mez je menší než -1 a horní mez je 0), vynásobte náhodné číslo nenulovou vazbou. Následující příklad to provede vygenerování 20 milionů náhodných čísel s plovoucí deseti čárkou, které jsou v rozsahu od 0 do Int64.MaxValue. Zobrazí se také rozdělení náhodných hodnot vygenerovaných metodou.
const long ONE_TENTH = 922337203685477581;
Random rnd = new Random();
double number;
int[] count = new int[10];
// Generate 20 million integer values between.
for (int ctr = 1; ctr <= 20000000; ctr++)
{
number = rnd.NextDouble() * Int64.MaxValue;
// Categorize random numbers into 10 groups.
count[(int)(number / ONE_TENTH)]++;
}
// Display breakdown by range.
Console.WriteLine("{0,28} {1,32} {2,7}\n", "Range", "Count", "Pct.");
for (int ctr = 0; ctr <= 9; ctr++)
Console.WriteLine("{0,25:N0}-{1,25:N0} {2,8:N0} {3,7:P2}", ctr * ONE_TENTH,
ctr < 9 ? ctr * ONE_TENTH + ONE_TENTH - 1 : Int64.MaxValue,
count[ctr], count[ctr] / 20000000.0);
// The example displays output like the following:
// Range Count Pct.
//
// 0- 922,337,203,685,477,580 1,996,148 9.98 %
// 922,337,203,685,477,581-1,844,674,407,370,955,161 2,000,293 10.00 %
// 1,844,674,407,370,955,162-2,767,011,611,056,432,742 2,000,094 10.00 %
// 2,767,011,611,056,432,743-3,689,348,814,741,910,323 2,000,159 10.00 %
// 3,689,348,814,741,910,324-4,611,686,018,427,387,904 1,999,552 10.00 %
// 4,611,686,018,427,387,905-5,534,023,222,112,865,485 1,998,248 9.99 %
// 5,534,023,222,112,865,486-6,456,360,425,798,343,066 2,000,696 10.00 %
// 6,456,360,425,798,343,067-7,378,697,629,483,820,647 2,001,637 10.01 %
// 7,378,697,629,483,820,648-8,301,034,833,169,298,228 2,002,870 10.01 %
// 8,301,034,833,169,298,229-9,223,372,036,854,775,807 2,000,303 10.00 %
[<Literal>]
let ONE_TENTH = 922337203685477581L
let rnd = Random()
// Generate 20 million random integers.
let count =
Array.init 20000000 (fun _ -> rnd.NextDouble() * (float Int64.MaxValue) )
|> Array.countBy (fun x -> x / (float ONE_TENTH) |> int ) // Categorize into 10 groups and count them.
|> Array.map snd
// Display breakdown by range.
printfn "%28s %32s %7s\n" "Range" "Count" "Pct."
for i = 0 to 9 do
let r1 = int64 i * ONE_TENTH
let r2 = if i < 9 then r1 + ONE_TENTH - 1L else Int64.MaxValue
printfn $"{r1,25:N0}-{r2,25:N0} {count.[i],8:N0} {float count.[i] / 20000000.0,7:P2}"
// The example displays output like the following:
// Range Count Pct.
//
// 0- 922,337,203,685,477,580 1,996,148 9.98 %
// 922,337,203,685,477,581-1,844,674,407,370,955,161 2,000,293 10.00 %
// 1,844,674,407,370,955,162-2,767,011,611,056,432,742 2,000,094 10.00 %
// 2,767,011,611,056,432,743-3,689,348,814,741,910,323 2,000,159 10.00 %
// 3,689,348,814,741,910,324-4,611,686,018,427,387,904 1,999,552 10.00 %
// 4,611,686,018,427,387,905-5,534,023,222,112,865,485 1,998,248 9.99 %
// 5,534,023,222,112,865,486-6,456,360,425,798,343,066 2,000,696 10.00 %
// 6,456,360,425,798,343,067-7,378,697,629,483,820,647 2,001,637 10.01 %
// 7,378,697,629,483,820,648-8,301,034,833,169,298,228 2,002,870 10.01 %
// 8,301,034,833,169,298,229-9,223,372,036,854,775,807 2,000,303 10.00 %
Module Example5
Public Sub Main()
Const ONE_TENTH As Long = 922337203685477581
Dim rnd As New Random()
Dim number As Long
Dim count(9) As Integer
' Generate 20 million integer values.
For ctr As Integer = 1 To 20000000
number = CLng(rnd.NextDouble() * Int64.MaxValue)
' Categorize random numbers.
count(CInt(number \ ONE_TENTH)) += 1
Next
' Display breakdown by range.
Console.WriteLine("{0,28} {1,32} {2,7}", "Range", "Count", "Pct.")
Console.WriteLine()
For ctr As Integer = 0 To 9
Console.WriteLine("{0,25:N0}-{1,25:N0} {2,8:N0} {3,7:P2}", ctr * ONE_TENTH,
If(ctr < 9, ctr * ONE_TENTH + ONE_TENTH - 1, Int64.MaxValue),
count(ctr), count(ctr) / 20000000)
Next
End Sub
End Module
' The example displays output like the following:
' Range Count Pct.
'
' 0- 922,337,203,685,477,580 1,996,148 9.98 %
' 922,337,203,685,477,581-1,844,674,407,370,955,161 2,000,293 10.00 %
' 1,844,674,407,370,955,162-2,767,011,611,056,432,742 2,000,094 10.00 %
' 2,767,011,611,056,432,743-3,689,348,814,741,910,323 2,000,159 10.00 %
' 3,689,348,814,741,910,324-4,611,686,018,427,387,904 1,999,552 10.00 %
' 4,611,686,018,427,387,905-5,534,023,222,112,865,485 1,998,248 9.99 %
' 5,534,023,222,112,865,486-6,456,360,425,798,343,066 2,000,696 10.00 %
' 6,456,360,425,798,343,067-7,378,697,629,483,820,647 2,001,637 10.01 %
' 7,378,697,629,483,820,648-8,301,034,833,169,298,228 2,002,870 10.01 %
' 8,301,034,833,169,298,229-9,223,372,036,854,775,807 2,000,303 10.00 %
K vygenerování náhodných čísel s plovoucí desetinou čárkou mezi dvěma libovolnými hodnotami, jako je Next(Int32, Int32) tomu u metody pro celá čísla, použijte následující vzorec:
Random.NextDouble() * (maxValue - minValue) + minValue
Následující příklad vygeneruje 1 milion náhodných čísel v rozsahu od 10,0 do 11,0 a zobrazí jejich rozdělení.
Random rnd = new Random();
int lowerBound = 10;
int upperBound = 11;
int[] range = new int[10];
for (int ctr = 1; ctr <= 1000000; ctr++)
{
Double value = rnd.NextDouble() * (upperBound - lowerBound) + lowerBound;
range[(int)Math.Truncate((value - lowerBound) * 10)]++;
}
for (int ctr = 0; ctr <= 9; ctr++)
{
Double lowerRange = 10 + ctr * .1;
Console.WriteLine("{0:N1} to {1:N1}: {2,8:N0} ({3,7:P2})",
lowerRange, lowerRange + .1, range[ctr],
range[ctr] / 1000000.0);
}
// The example displays output like the following:
// 10.0 to 10.1: 99,929 ( 9.99 %)
// 10.1 to 10.2: 100,189 (10.02 %)
// 10.2 to 10.3: 99,384 ( 9.94 %)
// 10.3 to 10.4: 100,240 (10.02 %)
// 10.4 to 10.5: 99,397 ( 9.94 %)
// 10.5 to 10.6: 100,580 (10.06 %)
// 10.6 to 10.7: 100,293 (10.03 %)
// 10.7 to 10.8: 100,135 (10.01 %)
// 10.8 to 10.9: 99,905 ( 9.99 %)
// 10.9 to 11.0: 99,948 ( 9.99 %)
let rnd = Random()
let lowerBound = 10.0
let upperBound = 11.0
let range =
Array.init 1000000 (fun _ -> rnd.NextDouble() * (upperBound - lowerBound) + lowerBound)
|> Array.countBy (fun x -> Math.Truncate((x - lowerBound) * 10.0) |> int)
|> Array.map snd
for i = 0 to 9 do
let lowerRange = 10.0 + float i * 0.1
printfn $"{lowerRange:N1} to {lowerRange + 0.1:N1}: {range.[i],8:N0} ({float range.[i] / 1000000.0,6:P2})"
// The example displays output like the following:
// 10.0 to 10.1: 99,929 ( 9.99 %)
// 10.1 to 10.2: 100,189 (10.02 %)
// 10.2 to 10.3: 99,384 ( 9.94 %)
// 10.3 to 10.4: 100,240 (10.02 %)
// 10.4 to 10.5: 99,397 ( 9.94 %)
// 10.5 to 10.6: 100,580 (10.06 %)
// 10.6 to 10.7: 100,293 (10.03 %)
// 10.7 to 10.8: 100,135 (10.01 %)
// 10.8 to 10.9: 99,905 ( 9.99 %)
// 10.9 to 11.0: 99,948 ( 9.99 %)
Module Example7
Public Sub Main()
Dim rnd As New Random()
Dim lowerBound As Integer = 10
Dim upperBound As Integer = 11
Dim range(9) As Integer
For ctr As Integer = 1 To 1000000
Dim value As Double = rnd.NextDouble() * (upperBound - lowerBound) + lowerBound
range(CInt(Math.Truncate((value - lowerBound) * 10))) += 1
Next
For ctr As Integer = 0 To 9
Dim lowerRange As Double = 10 + ctr * 0.1
Console.WriteLine("{0:N1} to {1:N1}: {2,8:N0} ({3,7:P2})",
lowerRange, lowerRange + 0.1, range(ctr),
range(ctr) / 1000000.0)
Next
End Sub
End Module
' The example displays output like the following:
' 10.0 to 10.1: 99,929 ( 9.99 %)
' 10.1 to 10.2: 100,189 (10.02 %)
' 10.2 to 10.3: 99,384 ( 9.94 %)
' 10.3 to 10.4: 100,240 (10.02 %)
' 10.4 to 10.5: 99,397 ( 9.94 %)
' 10.5 to 10.6: 100,580 (10.06 %)
' 10.6 to 10.7: 100,293 (10.03 %)
' 10.7 to 10.8: 100,135 (10.01 %)
' 10.8 to 10.9: 99,905 ( 9.99 %)
' 10.9 to 11.0: 99,948 ( 9.99 %)
Generování náhodných logických hodnot
Třída Random neposkytuje metody, které generují Boolean hodnoty. Můžete ale definovat vlastní třídu nebo metodu, abyste to mohli udělat. Následující příklad definuje třídu , BooleanGenerator
s jedinou metodou, NextBoolean
. Třída BooleanGenerator
ukládá Random objekt jako privátní proměnnou. Metoda NextBoolean
volá metodu Random.Next(Int32, Int32) a předá výsledek metodě Convert.ToBoolean(Int32) . Všimněte si, že 2 se používá jako argument k určení horní hranice náhodného čísla. Vzhledem k tomu, že se jedná o výhradní hodnotu, volání metody vrátí hodnotu 0 nebo 1.
using System;
public class Example1
{
public static void Main()
{
// Instantiate the Boolean generator.
BooleanGenerator boolGen = new BooleanGenerator();
int totalTrue = 0, totalFalse = 0;
// Generate 1,0000 random Booleans, and keep a running total.
for (int ctr = 0; ctr < 1000000; ctr++)
{
bool value = boolGen.NextBoolean();
if (value)
totalTrue++;
else
totalFalse++;
}
Console.WriteLine("Number of true values: {0,7:N0} ({1:P3})",
totalTrue,
((double)totalTrue) / (totalTrue + totalFalse));
Console.WriteLine("Number of false values: {0,7:N0} ({1:P3})",
totalFalse,
((double)totalFalse) / (totalTrue + totalFalse));
}
}
public class BooleanGenerator
{
Random rnd;
public BooleanGenerator()
{
rnd = new Random();
}
public bool NextBoolean()
{
return rnd.Next(0, 2) == 1;
}
}
// The example displays output like the following:
// Number of true values: 500,004 (50.000 %)
// Number of false values: 499,996 (50.000 %)
open System
type BooleanGenerator() =
let rnd = Random()
member _.NextBoolean() =
rnd.Next(0, 2) = 1
let boolGen = BooleanGenerator()
let mutable totalTrue, totalFalse = 0, 0
for _ = 1 to 1000000 do
let value = boolGen.NextBoolean()
if value then
totalTrue <- totalTrue + 1
else
totalFalse <- totalFalse + 1
printfn $"Number of true values: {totalTrue,7:N0} ({(double totalTrue) / double (totalTrue + totalFalse):P3})"
printfn $"Number of false values: {totalFalse,7:N0} ({(double totalFalse) / double (totalTrue + totalFalse):P3})"
// The example displays output like the following:
// Number of true values: 500,004 (50.000 %)
// Number of false values: 499,996 (50.000 %)
Module Example2
Public Sub Main()
' Instantiate the Boolean generator.
Dim boolGen As New BooleanGenerator()
Dim totalTrue, totalFalse As Integer
' Generate 1,0000 random Booleans, and keep a running total.
For ctr As Integer = 0 To 9999999
Dim value As Boolean = boolGen.NextBoolean()
If value Then
totalTrue += 1
Else
totalFalse += 1
End If
Next
Console.WriteLine("Number of true values: {0,7:N0} ({1:P3})",
totalTrue,
totalTrue / (totalTrue + totalFalse))
Console.WriteLine("Number of false values: {0,7:N0} ({1:P3})",
totalFalse,
totalFalse / (totalTrue + totalFalse))
End Sub
End Module
Public Class BooleanGenerator
Dim rnd As Random
Public Sub New()
rnd = New Random()
End Sub
Public Function NextBoolean() As Boolean
Return Convert.ToBoolean(rnd.Next(0, 2))
End Function
End Class
' The example displays the following output:
' Number of true values: 500,004 (50.000 %)
' Number of false values: 499,996 (50.000 %)
Místo vytvoření samostatné třídy pro generování náhodných Boolean hodnot by příklad mohl jednoduše definovat jednu metodu. V takovém případě by však měl být objekt definován jako proměnná na úrovni třídy, Random aby se zabránilo vytvoření instance nové Random instance v každém volání metody. V jazyce Visual Basic lze náhodnou instanci definovat jako statickou proměnnou v NextBoolean
metodě. Následující příklad poskytuje implementaci.
Random rnd = new Random();
int totalTrue = 0, totalFalse = 0;
// Generate 1,000,000 random Booleans, and keep a running total.
for (int ctr = 0; ctr < 1000000; ctr++)
{
bool value = NextBoolean();
if (value)
totalTrue++;
else
totalFalse++;
}
Console.WriteLine("Number of true values: {0,7:N0} ({1:P3})",
totalTrue,
((double)totalTrue) / (totalTrue + totalFalse));
Console.WriteLine("Number of false values: {0,7:N0} ({1:P3})",
totalFalse,
((double)totalFalse) / (totalTrue + totalFalse));
bool NextBoolean()
{
return rnd.Next(0, 2) == 1;
}
// The example displays output like the following:
// Number of true values: 499,777 (49.978 %)
// Number of false values: 500,223 (50.022 %)
let rnd = Random()
let nextBool () =
rnd.Next(0, 2) = 1
let mutable totalTrue, totalFalse = 0, 0
for _ = 1 to 1000000 do
let value = nextBool ()
if value then
totalTrue <- totalTrue + 1
else
totalFalse <- totalFalse + 1
printfn $"Number of true values: {totalTrue,7:N0} ({(double totalTrue) / double (totalTrue + totalFalse):P3})"
printfn $"Number of false values: {totalFalse,7:N0} ({(double totalFalse) / double (totalTrue + totalFalse):P3})"
// The example displays output like the following:
// Number of true values: 499,777 (49.978 %)
// Number of false values: 500,223 (50.022 %)
Module Example3
Public Sub Main()
Dim totalTrue, totalFalse As Integer
' Generate 1,0000 random Booleans, and keep a running total.
For ctr As Integer = 0 To 9999999
Dim value As Boolean = NextBoolean()
If value Then
totalTrue += 1
Else
totalFalse += 1
End If
Next
Console.WriteLine("Number of true values: {0,7:N0} ({1:P3})",
totalTrue,
totalTrue / (totalTrue + totalFalse))
Console.WriteLine("Number of false values: {0,7:N0} ({1:P3})",
totalFalse,
totalFalse / (totalTrue + totalFalse))
End Sub
Public Function NextBoolean() As Boolean
Static rnd As New Random()
Return Convert.ToBoolean(rnd.Next(0, 2))
End Function
End Module
' The example displays the following output:
' Number of true values: 499,777 (49.978 %)
' Number of false values: 500,223 (50.022 %)
Generování náhodných 64bitových celých čísel
Přetížení Next metody vrací 32bitové celá čísla. V některých případech ale můžete chtít pracovat s 64bitovými celými čísly. Můžete to udělat takto:
NextDouble Voláním metody načtěte hodnotu s plovoucí desetinnou čárkou s dvojitou přesností.
Vynásobte danou hodnotu hodnotou Int64.MaxValue.
Následující příklad používá tuto techniku ke generování 20 milionů náhodných dlouhých celých čísel a kategorizuje je ve 10 stejných skupinách. Pak vyhodnotí rozdělení náhodných čísel tím, že spočítá číslo v každé skupině od 0 do Int64.MaxValue. Jak ukazuje výstup z příkladu, čísla se distribuují více nebo méně rovnoměrně v rozsahu dlouhého celého čísla.
const long ONE_TENTH = 922337203685477581;
Random rnd = new Random();
long number;
int[] count = new int[10];
// Generate 20 million long integers.
for (int ctr = 1; ctr <= 20000000; ctr++)
{
number = (long)(rnd.NextDouble() * Int64.MaxValue);
// Categorize random numbers.
count[(int)(number / ONE_TENTH)]++;
}
// Display breakdown by range.
Console.WriteLine("{0,28} {1,32} {2,7}\n", "Range", "Count", "Pct.");
for (int ctr = 0; ctr <= 9; ctr++)
Console.WriteLine("{0,25:N0}-{1,25:N0} {2,8:N0} {3,7:P2}", ctr * ONE_TENTH,
ctr < 9 ? ctr * ONE_TENTH + ONE_TENTH - 1 : Int64.MaxValue,
count[ctr], count[ctr] / 20000000.0);
// The example displays output like the following:
// Range Count Pct.
//
// 0- 922,337,203,685,477,580 1,996,148 9.98 %
// 922,337,203,685,477,581-1,844,674,407,370,955,161 2,000,293 10.00 %
// 1,844,674,407,370,955,162-2,767,011,611,056,432,742 2,000,094 10.00 %
// 2,767,011,611,056,432,743-3,689,348,814,741,910,323 2,000,159 10.00 %
// 3,689,348,814,741,910,324-4,611,686,018,427,387,904 1,999,552 10.00 %
// 4,611,686,018,427,387,905-5,534,023,222,112,865,485 1,998,248 9.99 %
// 5,534,023,222,112,865,486-6,456,360,425,798,343,066 2,000,696 10.00 %
// 6,456,360,425,798,343,067-7,378,697,629,483,820,647 2,001,637 10.01 %
// 7,378,697,629,483,820,648-8,301,034,833,169,298,228 2,002,870 10.01 %
// 8,301,034,833,169,298,229-9,223,372,036,854,775,807 2,000,303 10.00 %
[<Literal>]
let ONE_TENTH = 922337203685477581L
let rnd = Random()
let count =
// Generate 20 million random long integers.
Array.init 20000000 (fun _ -> rnd.NextDouble() * (float Int64.MaxValue) |> int64 )
|> Array.countBy (fun x -> x / ONE_TENTH) // Categorize and count random numbers.
|> Array.map snd
// Display breakdown by range.
printfn "%28s %32s %7s\n" "Range" "Count" "Pct."
for i = 0 to 9 do
let r1 = int64 i * ONE_TENTH
let r2 = if i < 9 then r1 + ONE_TENTH - 1L else Int64.MaxValue
printfn $"{r1,25:N0}-{r2,25:N0} {count.[i],8:N0} {float count.[i] / 20000000.0,7:P2}"
// The example displays output like the following:
// Range Count Pct.
//
// 0- 922,337,203,685,477,580 1,996,148 9.98 %
// 922,337,203,685,477,581-1,844,674,407,370,955,161 2,000,293 10.00 %
// 1,844,674,407,370,955,162-2,767,011,611,056,432,742 2,000,094 10.00 %
// 2,767,011,611,056,432,743-3,689,348,814,741,910,323 2,000,159 10.00 %
// 3,689,348,814,741,910,324-4,611,686,018,427,387,904 1,999,552 10.00 %
// 4,611,686,018,427,387,905-5,534,023,222,112,865,485 1,998,248 9.99 %
// 5,534,023,222,112,865,486-6,456,360,425,798,343,066 2,000,696 10.00 %
// 6,456,360,425,798,343,067-7,378,697,629,483,820,647 2,001,637 10.01 %
// 7,378,697,629,483,820,648-8,301,034,833,169,298,228 2,002,870 10.01 %
// 8,301,034,833,169,298,229-9,223,372,036,854,775,807 2,000,303 10.00 %
Module Example8
Public Sub Main()
Const ONE_TENTH As Long = 922337203685477581
Dim rnd As New Random()
Dim number As Long
Dim count(9) As Integer
' Generate 20 million long integers.
For ctr As Integer = 1 To 20000000
number = CLng(rnd.NextDouble() * Int64.MaxValue)
' Categorize random numbers.
count(CInt(number \ ONE_TENTH)) += 1
Next
' Display breakdown by range.
Console.WriteLine("{0,28} {1,32} {2,7}", "Range", "Count", "Pct.")
Console.WriteLine()
For ctr As Integer = 0 To 9
Console.WriteLine("{0,25:N0}-{1,25:N0} {2,8:N0} {3,7:P2}", ctr * ONE_TENTH,
If(ctr < 9, ctr * ONE_TENTH + ONE_TENTH - 1, Int64.MaxValue),
count(ctr), count(ctr) / 20000000)
Next
End Sub
End Module
' The example displays output like the following:
' Range Count Pct.
'
' 0- 922,337,203,685,477,580 1,996,148 9.98 %
' 922,337,203,685,477,581-1,844,674,407,370,955,161 2,000,293 10.00 %
' 1,844,674,407,370,955,162-2,767,011,611,056,432,742 2,000,094 10.00 %
' 2,767,011,611,056,432,743-3,689,348,814,741,910,323 2,000,159 10.00 %
' 3,689,348,814,741,910,324-4,611,686,018,427,387,904 1,999,552 10.00 %
' 4,611,686,018,427,387,905-5,534,023,222,112,865,485 1,998,248 9.99 %
' 5,534,023,222,112,865,486-6,456,360,425,798,343,066 2,000,696 10.00 %
' 6,456,360,425,798,343,067-7,378,697,629,483,820,647 2,001,637 10.01 %
' 7,378,697,629,483,820,648-8,301,034,833,169,298,228 2,002,870 10.01 %
' 8,301,034,833,169,298,229-9,223,372,036,854,775,807 2,000,303 10.00 %
Alternativní technika, která používá bitovou manipulaci, nevygeneruje skutečně náhodná čísla. Tato technika volá Next() , aby se vygenerovaly dvě celá čísla, posune se zleva o 32 bitů a dají se dohromady. Tato technika má dvě omezení:
Protože bit 31 je bit znaménka, hodnota v bitu 31 výsledného dlouhého celého čísla je vždy 0. Tento problém lze vyřešit generováním náhodné hodnoty 0 nebo 1, posunutím doleva o 31 bitů a oringováním s původním náhodným dlouhým celé číslo.
Vážněji, protože pravděpodobnost, že hodnota vrácená Next() hodnotou bude 0, bude v oblasti 0x0-0x00000000FFFFFFFF několik náhodných čísel.
Načtení bajtů v zadaném rozsahu
Přetížení Next metody umožňují určit rozsah náhodných čísel, ale NextBytes metoda ne. Následující příklad implementuje metodu NextBytes
, která umožňuje zadat rozsah vrácených bajtů. Random2
Definuje třídu, která je odvozena a Random přetíží její NextBytes
metodu.
using System;
public class Example3
{
public static void Main()
{
Random2 rnd = new Random2();
Byte[] bytes = new Byte[10000];
int[] total = new int[101];
rnd.NextBytes(bytes, 0, 101);
// Calculate how many of each value we have.
foreach (var value in bytes)
total[value]++;
// Display the results.
for (int ctr = 0; ctr < total.Length; ctr++)
{
Console.Write("{0,3}: {1,-3} ", ctr, total[ctr]);
if ((ctr + 1) % 5 == 0) Console.WriteLine();
}
}
}
public class Random2 : Random
{
public Random2() : base()
{ }
public Random2(int seed) : base(seed)
{ }
public void NextBytes(byte[] bytes, byte minValue, byte maxValue)
{
for (int ctr = bytes.GetLowerBound(0); ctr <= bytes.GetUpperBound(0); ctr++)
bytes[ctr] = (byte)Next(minValue, maxValue);
}
}
// The example displays output like the following:
// 0: 115 1: 119 2: 92 3: 98 4: 92
// 5: 102 6: 103 7: 84 8: 93 9: 116
// 10: 91 11: 98 12: 106 13: 91 14: 92
// 15: 101 16: 100 17: 96 18: 97 19: 100
// 20: 101 21: 106 22: 112 23: 82 24: 85
// 25: 102 26: 107 27: 98 28: 106 29: 102
// 30: 109 31: 108 32: 94 33: 101 34: 107
// 35: 101 36: 86 37: 100 38: 101 39: 102
// 40: 113 41: 95 42: 96 43: 89 44: 99
// 45: 81 46: 89 47: 105 48: 100 49: 85
// 50: 103 51: 103 52: 93 53: 89 54: 91
// 55: 97 56: 105 57: 97 58: 110 59: 86
// 60: 116 61: 94 62: 117 63: 98 64: 110
// 65: 93 66: 102 67: 100 68: 105 69: 83
// 70: 81 71: 97 72: 85 73: 70 74: 98
// 75: 100 76: 110 77: 114 78: 83 79: 90
// 80: 96 81: 112 82: 102 83: 102 84: 99
// 85: 81 86: 100 87: 93 88: 99 89: 118
// 90: 95 91: 124 92: 108 93: 96 94: 104
// 95: 106 96: 99 97: 99 98: 92 99: 99
// 100: 108
open System
type Random2() =
inherit Random()
member this.NextBytes(bytes: byte[], minValue: byte, maxValue: byte) =
for i=bytes.GetLowerBound(0) to bytes.GetUpperBound(0) do
bytes.[i] <- this.Next(int minValue, int maxValue) |> byte
let rnd = Random2()
let bytes = Array.zeroCreate 10000
let total = Array.zeroCreate 101
rnd.NextBytes(bytes, 0uy, 101uy)
// Calculate how many of each value we have.
for v in bytes do
total.[int v] <- total.[int v] + 1
// Display the results.
for i = 0 to total.Length - 1 do
printf "%3i: %-3i " i total.[i]
if (i + 1) % 5 = 0 then printfn ""
// The example displays output like the following:
// 0: 115 1: 119 2: 92 3: 98 4: 92
// 5: 102 6: 103 7: 84 8: 93 9: 116
// 10: 91 11: 98 12: 106 13: 91 14: 92
// 15: 101 16: 100 17: 96 18: 97 19: 100
// 20: 101 21: 106 22: 112 23: 82 24: 85
// 25: 102 26: 107 27: 98 28: 106 29: 102
// 30: 109 31: 108 32: 94 33: 101 34: 107
// 35: 101 36: 86 37: 100 38: 101 39: 102
// 40: 113 41: 95 42: 96 43: 89 44: 99
// 45: 81 46: 89 47: 105 48: 100 49: 85
// 50: 103 51: 103 52: 93 53: 89 54: 91
// 55: 97 56: 105 57: 97 58: 110 59: 86
// 60: 116 61: 94 62: 117 63: 98 64: 110
// 65: 93 66: 102 67: 100 68: 105 69: 83
// 70: 81 71: 97 72: 85 73: 70 74: 98
// 75: 100 76: 110 77: 114 78: 83 79: 90
// 80: 96 81: 112 82: 102 83: 102 84: 99
// 85: 81 86: 100 87: 93 88: 99 89: 118
// 90: 95 91: 124 92: 108 93: 96 94: 104
// 95: 106 96: 99 97: 99 98: 92 99: 99
// 100: 108
Module Example4
Public Sub Main()
Dim rnd As New Random2()
Dim bytes(9999) As Byte
Dim total(100) As Integer
rnd.NextBytes(bytes, 0, 101)
' Calculate how many of each value we have.
For Each value In bytes
total(value) += 1
Next
' Display the results.
For ctr As Integer = 0 To total.Length - 1
Console.Write("{0,3}: {1,-3} ", ctr, total(ctr))
If (ctr + 1) Mod 5 = 0 Then Console.WriteLine()
Next
End Sub
End Module
Public Class Random2 : Inherits Random
Public Sub New()
MyBase.New()
End Sub
Public Sub New(seed As Integer)
MyBase.New(seed)
End Sub
Public Overloads Sub NextBytes(bytes() As Byte,
minValue As Byte, maxValue As Byte)
For ctr As Integer = bytes.GetLowerbound(0) To bytes.GetUpperBound(0)
bytes(ctr) = CByte(MyBase.Next(minValue, maxValue))
Next
End Sub
End Class
' The example displays output like the following:
' 0: 115 1: 119 2: 92 3: 98 4: 92
' 5: 102 6: 103 7: 84 8: 93 9: 116
' 10: 91 11: 98 12: 106 13: 91 14: 92
' 15: 101 16: 100 17: 96 18: 97 19: 100
' 20: 101 21: 106 22: 112 23: 82 24: 85
' 25: 102 26: 107 27: 98 28: 106 29: 102
' 30: 109 31: 108 32: 94 33: 101 34: 107
' 35: 101 36: 86 37: 100 38: 101 39: 102
' 40: 113 41: 95 42: 96 43: 89 44: 99
' 45: 81 46: 89 47: 105 48: 100 49: 85
' 50: 103 51: 103 52: 93 53: 89 54: 91
' 55: 97 56: 105 57: 97 58: 110 59: 86
' 60: 116 61: 94 62: 117 63: 98 64: 110
' 65: 93 66: 102 67: 100 68: 105 69: 83
' 70: 81 71: 97 72: 85 73: 70 74: 98
' 75: 100 76: 110 77: 114 78: 83 79: 90
' 80: 96 81: 112 82: 102 83: 102 84: 99
' 85: 81 86: 100 87: 93 88: 99 89: 118
' 90: 95 91: 124 92: 108 93: 96 94: 104
' 95: 106 96: 99 97: 99 98: 92 99: 99
' 100: 108
Metoda NextBytes(Byte[], Byte, Byte)
zabalí volání Next(Int32, Int32) metody a určuje minimální hodnotu a jednu větší než maximální hodnotu (v tomto případě 0 a 101), kterou chceme vrátit v bajtovém poli. Protože jsme si jisti, že celočíselné hodnoty vrácené metodou Next jsou v rozsahu Byte datového typu, můžeme je bezpečně přetypovat (v jazyce C# a F#) nebo je převést (v jazyce Visual Basic) z celých čísel na bajty.
Náhodné načtení prvku z pole nebo kolekce
Náhodná čísla často slouží jako indexy pro načtení hodnot z polí nebo kolekcí. Chcete-li načíst náhodnou hodnotu indexu, můžete volat metodu Next(Int32, Int32) a použít dolní mez matice jako hodnotu argumentu minValue
a jednu větší než horní mez matice jako hodnotu argumentu maxValue
. U pole založeného na nule je to ekvivalentní jeho Length vlastnosti nebo jedna větší než hodnota vrácená metodou Array.GetUpperBound . Následující příklad náhodně načte název města v USA z pole měst.
String[] cities = { "Atlanta", "Boston", "Chicago", "Detroit",
"Fort Wayne", "Greensboro", "Honolulu", "Indianapolis",
"Jersey City", "Kansas City", "Los Angeles",
"Milwaukee", "New York", "Omaha", "Philadelphia",
"Raleigh", "San Francisco", "Tulsa", "Washington" };
Random rnd = new Random();
int index = rnd.Next(0, cities.Length);
Console.WriteLine("Today's city of the day: {0}",
cities[index]);
// The example displays output like the following:
// Today's city of the day: Honolulu
let cities =
[| "Atlanta"; "Boston"; "Chicago"; "Detroit";
"Fort Wayne"; "Greensboro"; "Honolulu"; "Indianapolis";
"Jersey City"; "Kansas City"; "Los Angeles";
"Milwaukee"; "New York"; "Omaha"; "Philadelphia";
"Raleigh"; "San Francisco"; "Tulsa"; "Washington" |]
let rnd = Random()
let index = rnd.Next(0,cities.Length)
printfn "Today's city of the day: %s" cities.[index]
// The example displays output like the following:
// Today's city of the day: Honolulu
Module Example1
Public Sub Main()
Dim cities() As String = {"Atlanta", "Boston", "Chicago", "Detroit",
"Fort Wayne", "Greensboro", "Honolulu", "Indianapolis",
"Jersey City", "Kansas City", "Los Angeles",
"Milwaukee", "New York", "Omaha", "Philadelphia",
"Raleigh", "San Francisco", "Tulsa", "Washington"}
Dim rnd As New Random()
Dim index As Integer = rnd.Next(0, cities.Length)
Console.WriteLine("Today's city of the day: {0}",
cities(index))
End Sub
End Module
' The example displays output like the following:
' Today's city of the day: Honolulu
Načtení jedinečného prvku z pole nebo kolekce
Generátor náhodných čísel může vždy vracet duplicitní hodnoty. S tím, jak se rozsah čísel zmenší nebo se zvětší počet vygenerovaných hodnot, bude pravděpodobnost duplicit růst. Pokud musí být náhodné hodnoty jedinečné, vygenerují se další čísla, která kompenzují duplicity, což vede ke stále horšímu výkonu.
Tento scénář je možné zpracovat několika technikami. Jedním z běžných řešení je vytvoření pole nebo kolekce obsahující hodnoty, které se mají načíst, a paralelní matice obsahující náhodná čísla s plovoucí desetinou čárkou. Druhé pole se naplní náhodnými čísly v okamžiku vytvoření prvního pole a Array.Sort(Array, Array) metoda se použije k seřazení prvního pole pomocí hodnot v paralelním poli.
Pokud například vyvíjíte hru Solitaire, chcete zajistit, aby se každá karta používala jen jednou. Místo generování náhodných čísel pro načtení karty a sledování toho, jestli už byla tato karta uvolněna, můžete vytvořit paralelní pole náhodných čísel, která se dají použít k řazení balíčku. Jakmile je balíček seřazený, může vaše aplikace udržovat ukazatel, který označuje index další karty v balíčku.
Tento postup znázorňuje následující příklad. Definuje Card
třídu, která představuje hrací kartu a Dealer
třídu, která se zabývá balíček prohazovaných karet. Konstruktor Dealer
třídy naplní dvě pole: deck
pole, které má obor třídy a který představuje všechny karty v balíčku; a místní order
pole, které má stejný počet prvků jako deck
pole a je naplněn náhodně vygenerovanými Double hodnotami. Metoda Array.Sort(Array, Array) se pak zavolá k seřazení deck
pole na základě hodnot v matici 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}", this.FaceValue, this.Suit);
}
}
public enum Suit { Hearts, Diamonds, Spades, Clubs };
public enum FaceValue
{
Ace = 1, Two, Three, Four, Five, Six,
Seven, Eight, Nine, Ten, Jack, Queen,
King
};
public class Dealer
{
Random rnd;
// A deck of cards, without Jokers.
Card[] deck = new Card[52];
// Parallel array for sorting cards.
Double[] order = new Double[52];
// A pointer to the next card to deal.
int ptr = 0;
// A flag to indicate the deck is used.
bool mustReshuffle = false;
public Dealer()
{
rnd = new Random();
// Initialize the deck.
int deckCtr = 0;
foreach (var suit in Enum.GetValues(typeof(Suit)))
{
foreach (var faceValue in Enum.GetValues(typeof(FaceValue)))
{
Card card = new Card();
card.Suit = (Suit)suit;
card.FaceValue = (FaceValue)faceValue;
deck[deckCtr] = card;
deckCtr++;
}
}
for (int ctr = 0; ctr < order.Length; ctr++)
order[ctr] = rnd.NextDouble();
Array.Sort(order, deck);
}
public Card[] Deal(int numberToDeal)
{
if (mustReshuffle)
{
Console.WriteLine("There are no cards left in the deck");
return null;
}
Card[] cardsDealt = new Card[numberToDeal];
for (int ctr = 0; ctr < numberToDeal; ctr++)
{
cardsDealt[ctr] = deck[ptr];
ptr++;
if (ptr == deck.Length)
mustReshuffle = true;
if (mustReshuffle & ctr < numberToDeal - 1)
{
Console.WriteLine("Can only deal the {0} cards remaining on the deck.",
ctr + 1);
return cardsDealt;
}
}
return cardsDealt;
}
}
public class Example17
{
public static void Main()
{
Dealer dealer = new Dealer();
ShowCards(dealer.Deal(20));
}
private static void ShowCards(Card[] cards)
{
foreach (var card in cards)
if (card != null)
Console.WriteLine("{0} of {1}", card.FaceValue, card.Suit);
}
}
// The example displays output like the following:
// Six of Diamonds
// King of Clubs
// Eight of Clubs
// Seven of Clubs
// Queen of Clubs
// King of Hearts
// Three of Spades
// Ace of Clubs
// Four of Hearts
// Three of Diamonds
// Nine of Diamonds
// Two of Hearts
// Ace of Hearts
// Three of Hearts
// Four of Spades
// Eight of Hearts
// Queen of Diamonds
// Two of Clubs
// Four of Diamonds
// Jack of Hearts
open System
type Suit =
| Clubs
| Diamonds
| Hearts
| Spades
type Face =
| Ace | Two | Three
| Four | Five | Six
| Seven | Eight | Nine
| Ten | Jack | Queen | King
type Card = { Face: Face; Suit: Suit }
let suits = [ Clubs; Diamonds; Hearts; Spades ]
let faces = [ Ace; Two; Three; Four; Five; Six; Seven; Eight; Nine; Ten; Jack; Queen; King ]
type Dealer() =
let rnd = Random()
let mutable pos = 0
// Parallel array for sorting cards.
let order = Array.init (suits.Length * faces.Length) (fun _ -> rnd.NextDouble() )
// A deck of cards, without Jokers.
let deck = [|
for s in suits do
for f in faces do
{ Face = f; Suit = s } |]
// Shuffle the deck.
do Array.Sort(order, deck)
// Deal a number of cards from the deck, return None if failed
member _.Deal(numberToDeal) : Card [] option =
if numberToDeal = 0 || pos = deck.Length then
printfn "There are no cards left in the deck"
None
else
let cards = deck.[pos .. numberToDeal + pos - 1]
if numberToDeal > deck.Length - pos then
printfn "Can only deal the %i cards remaining on the deck." (deck.Length - pos)
pos <- min (pos + numberToDeal) deck.Length
Some cards
let showCards cards =
for card in cards do
printfn $"{card.Face} of {card.Suit}"
let dealer = Dealer()
dealer.Deal 20
|> Option.iter showCards
// The example displays output like the following:
// Six of Diamonds
// King of Clubs
// Eight of Clubs
// Seven of Clubs
// Queen of Clubs
// King of Hearts
// Three of Spades
// Ace of Clubs
// Four of Hearts
// Three of Diamonds
// Nine of Diamonds
// Two of Hearts
// Ace of Hearts
// Three of Hearts
// Four of Spades
// Eight of Hearts
// Queen of Diamonds
// Two of Clubs
// Four of Diamonds
// Jack of Hearts
' A class that represents an individual card in a playing deck.
Public Class Card
Public Suit As Suit
Public FaceValue As FaceValue
Public Overrides Function ToString() As String
Return String.Format("{0:F} of {1:F}", Me.FaceValue, Me.Suit)
End Function
End Class
Public Enum Suit As Integer
Hearts = 0
Diamonds = 1
Spades = 2
Clubs = 3
End Enum
Public Enum FaceValue As Integer
Ace = 1
Two = 2
Three = 3
Four = 4
Five = 5
Six = 6
Seven = 7
Eight = 8
Nine = 9
Ten = 10
Jack = 11
Queen = 12
King = 13
End Enum
Public Class Dealer
Dim rnd As Random
' A deck of cards, without Jokers.
Dim deck(51) As Card
' Parallel array for sorting cards.
Dim order(51) As Double
' A pointer to the next card to deal.
Dim ptr As Integer = 0
' A flag to indicate the deck is used.
Dim mustReshuffle As Boolean
Public Sub New()
rnd = New Random()
' Initialize the deck.
Dim deckCtr As Integer = 0
For Each Suit In [Enum].GetValues(GetType(Suit))
For Each faceValue In [Enum].GetValues(GetType(FaceValue))
Dim card As New Card()
card.Suit = CType(Suit, Suit)
card.FaceValue = CType(faceValue, FaceValue)
deck(deckCtr) = card
deckCtr += 1
Next
Next
For ctr As Integer = 0 To order.Length - 1
order(ctr) = rnd.NextDouble()
Next
Array.Sort(order, deck)
End Sub
Public Function Deal(numberToDeal As Integer) As Card()
If mustReshuffle Then
Console.WriteLine("There are no cards left in the deck")
Return Nothing
End If
Dim cardsDealt(numberToDeal - 1) As Card
For ctr As Integer = 0 To numberToDeal - 1
cardsDealt(ctr) = deck(ptr)
ptr += 1
If ptr = deck.Length Then
mustReshuffle = True
End If
If mustReshuffle And ctr < numberToDeal - 1
Console.WriteLine("Can only deal the {0} cards remaining on the deck.",
ctr + 1)
Return cardsDealt
End If
Next
Return cardsDealt
End Function
End Class
Public Module Example
Public Sub Main()
Dim dealer As New Dealer()
ShowCards(dealer.Deal(20))
End Sub
Private Sub ShowCards(cards() As Card)
For Each card In cards
If card IsNot Nothing Then _
Console.WriteLine("{0} of {1}", card.FaceValue, card.Suit)
Next
End Sub
End Module
' The example displays output like the following:
' Six of Diamonds
' King of Clubs
' Eight of Clubs
' Seven of Clubs
' Queen of Clubs
' King of Hearts
' Three of Spades
' Ace of Clubs
' Four of Hearts
' Three of Diamonds
' Nine of Diamonds
' Two of Hearts
' Ace of Hearts
' Three of Hearts
' Four of Spades
' Eight of Hearts
' Queen of Diamonds
' Two of Clubs
' Four of Diamonds
' Jack of Hearts