Classe System.Random
Cet article vous offre des remarques complémentaires à la documentation de référence pour cette API.
La Random classe représente un générateur de nombres pseudo-aléatoires, qui est un algorithme qui produit une séquence de nombres qui répondent à certaines exigences statistiques pour la randomité.
Les nombres pseudo-aléatoires sont choisis avec une probabilité égale à partir d’un ensemble fini de nombres. Les nombres choisis ne sont pas complètement aléatoires, car un algorithme mathématique est utilisé pour les sélectionner, mais ils sont suffisamment aléatoires à des fins pratiques. L’implémentation de la Random classe est basée sur une version modifiée de l’algorithme générateur de nombres aléatoires soustractifs de Donald E. Knuth. Pour plus d’informations, consultez D. E. Knuth. Art of Computer Programming, Volume 2 : Algorithmes semi-numériques. Addison-Wesley, Reading, MA, troisième édition, 1997.
Pour générer un nombre aléatoire sécurisé par chiffrement, tel qu’un numéro adapté à la création d’un mot de passe aléatoire, utilisez l’une des méthodes statiques de la System.Security.Cryptography.RandomNumberGenerator classe.
Instancier le générateur de nombres aléatoires
Vous instanciez le générateur de nombres aléatoires en fournissant une valeur initiale (valeur de départ pour l’algorithme de génération de nombre pseudo-aléatoire) à un Random constructeur de classe. Vous pouvez fournir la valeur initiale explicitement ou implicitement :
- Le Random(Int32) constructeur utilise une valeur initiale explicite que vous fournissez.
- Le Random() constructeur utilise la valeur initiale par défaut. Il s’agit du moyen le plus courant d’instancier le générateur de nombres aléatoires.
Dans .NET Framework, la valeur initiale par défaut dépend du temps. Dans .NET Core, la valeur initiale par défaut est produite par le générateur de nombres pseudo-aléatoires statiques de thread.
Si la même valeur initiale est utilisée pour des objets distincts Random , elles génèrent la même série de nombres aléatoires. Cela peut être utile pour créer une suite de tests qui traite des valeurs aléatoires ou pour relire des jeux qui dérivent leurs données de nombres aléatoires. Toutefois, notez que Random les objets dans les processus exécutés sous différentes versions de .NET Framework peuvent retourner différentes séries de nombres aléatoires même s’ils sont instanciés avec des valeurs initiales identiques.
Pour produire différentes séquences de nombres aléatoires, vous pouvez rendre la valeur initiale dépendante du temps, ce qui produit une série différente avec chaque nouvelle instance de Random. Le constructeur paramétrable Random(Int32) peut prendre une Int32 valeur en fonction du nombre de graduations dans l’heure actuelle, tandis que le constructeur sans Random() paramètre utilise l’horloge système pour générer sa valeur initiale. Toutefois, sur .NET Framework uniquement, étant donné que l’horloge a une résolution finie, l’utilisation du constructeur sans paramètre pour créer différents Random objets dans une succession proche crée des générateurs de nombres aléatoires qui produisent des séquences identiques de nombres aléatoires. L’exemple suivant montre comment deux Random objets instanciés dans une succession proche dans une application .NET Framework génèrent une série identique de nombres aléatoires. Sur la plupart des systèmes Windows, Random les objets créés dans les 15 millisecondes d’une autre sont susceptibles d’avoir des valeurs initiales identiques.
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
Pour éviter ce problème, créez un objet unique Random au lieu de plusieurs objets. Notez que la Random
classe dans .NET Core n’a pas cette limitation.
Éviter plusieurs instanciations
Sur .NET Framework, l’initialisation de deux générateurs de nombres aléatoires dans une boucle serrée ou dans une succession rapide crée deux générateurs de nombres aléatoires qui peuvent produire des séquences identiques de nombres aléatoires. Dans la plupart des cas, ce n’est pas l’intention du développeur et peut entraîner des problèmes de performances, car l’instanciation et l’initialisation d’un générateur de nombres aléatoires est un processus relativement coûteux.
Pour améliorer les performances et éviter de créer par inadvertance des générateurs de nombres aléatoires distincts qui génèrent des séquences numériques identiques, nous vous recommandons de créer un Random objet pour générer de nombreux nombres aléatoires au fil du temps, au lieu de créer de nouveaux Random objets pour générer un nombre aléatoire.
Toutefois, la Random classe n’est pas thread-safe. Si vous appelez Random des méthodes à partir de plusieurs threads, suivez les instructions décrites dans la section suivante.
Sécurité des threads
Au lieu d’instancier des objets individuels Random , nous vous recommandons de créer une instance unique Random pour générer tous les nombres aléatoires nécessaires par votre application. Toutefois, Random les objets ne sont pas thread-safe. Si votre application appelle Random des méthodes à partir de plusieurs threads, vous devez utiliser un objet de synchronisation pour vous assurer qu’un seul thread peut accéder au générateur de nombres aléatoires à la fois. Si vous ne vérifiez pas que l’objet Random est accessible de manière thread-safe, les appels aux méthodes qui retournent des nombres aléatoires retournent 0.
L’exemple suivant utilise l’instruction de verrouillage C#, la fonction de verrouillage F# et l’instruction Visual Basic SyncLock pour vous assurer qu’un seul générateur de nombres aléatoires est accessible par 11 threads de manière thread-safe. Chaque thread génère 2 millions de nombres aléatoires, compte le nombre de nombres aléatoires générés et calcule leur somme, puis met à jour les totaux pour tous les threads une fois l’exécution terminée.
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
L’exemple garantit la sécurité des threads de la manière suivante :
- L’attribut ThreadStaticAttribute est utilisé pour définir des variables locales de thread qui suivent le nombre total de nombres aléatoires générés et leur somme pour chaque thread.
- Un verrou (l’instruction
lock
en C#, lalock
fonction en F# et l’instructionSyncLock
en Visual Basic) protège l’accès aux variables pour le nombre total et la somme de tous les nombres aléatoires générés sur tous les threads. - Un sémaphore (l’objet CountdownEvent ) est utilisé pour s’assurer que le thread principal bloque jusqu’à ce que tous les autres threads terminent l’exécution.
- L’exemple vérifie si le générateur de nombres aléatoires est endommagé en déterminant si deux appels consécutifs aux méthodes de génération de nombres aléatoires retournent 0. Si une altération est détectée, l’exemple utilise l’objet CancellationTokenSource pour signaler que tous les threads doivent être annulés.
- Avant de générer chaque nombre aléatoire, chaque thread vérifie l’état de l’objet CancellationToken . Si l’annulation est demandée, l’exemple appelle la CancellationToken.ThrowIfCancellationRequested méthode pour annuler le thread.
L’exemple suivant est identique au premier, sauf qu’il utilise un Task objet et une expression lambda au lieu d’objets Thread .
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
public class Example15
{
static Object randLock, numericLock;
static Random rand;
static CancellationTokenSource source;
double totalValue = 0.0;
int totalCount = 0;
public Example15()
{
rand = new Random();
randLock = new Object();
numericLock = new Object();
source = new CancellationTokenSource();
}
public static async Task Main()
{
Example15 ex = new Example15();
Thread.CurrentThread.Name = "Main";
await ex.Execute();
}
private async Task Execute()
{
List<Task> tasks = new List<Task>();
for (int ctr = 0; ctr <= 10; ctr++)
{
CancellationToken token = source.Token;
int taskNo = ctr;
tasks.Add(Task.Run(() =>
{
double previous = 0.0;
int taskCtr = 0;
double taskTotal = 0.0;
double result = 0.0;
for (int n = 0; n < 2000000; n++)
{
// Make sure there's no corruption of Random.
token.ThrowIfCancellationRequested();
lock (randLock)
{
result = rand.NextDouble();
}
// Check for corruption of Random instance.
if ((result == previous) && result == 0)
{
source.Cancel();
}
else
{
previous = result;
}
taskCtr++;
taskTotal += result;
}
// Show result.
Console.WriteLine("Task {0} finished execution.", taskNo);
Console.WriteLine("Random numbers generated: {0:N0}", taskCtr);
Console.WriteLine("Sum of random numbers: {0:N2}", taskTotal);
Console.WriteLine("Random number mean: {0:N4}\n", taskTotal / taskCtr);
// Update overall totals.
lock (numericLock)
{
totalCount += taskCtr;
totalValue += taskTotal;
}
},
token));
}
try
{
await Task.WhenAll(tasks.ToArray());
Console.WriteLine("\nTotal random numbers generated: {0:N0}", totalCount);
Console.WriteLine("Total sum of all random numbers: {0:N2}", totalValue);
Console.WriteLine("Random number mean: {0:N4}", totalValue / totalCount);
}
catch (AggregateException e)
{
foreach (Exception inner in e.InnerExceptions)
{
TaskCanceledException canc = inner as TaskCanceledException;
if (canc != null)
Console.WriteLine("Task #{0} cancelled.", canc.Task.Id);
else
Console.WriteLine("Exception: {0}", inner.GetType().Name);
}
}
finally
{
source.Dispose();
}
}
}
// The example displays output like the following:
// Task 1 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 1,000,502.47
// Random number mean: 0.5003
//
// Task 0 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 1,000,445.63
// Random number mean: 0.5002
//
// Task 2 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 1,000,556.04
// Random number mean: 0.5003
//
// Task 3 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 1,000,178.87
// Random number mean: 0.5001
//
// Task 4 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 999,819.17
// Random number mean: 0.4999
//
// Task 5 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 1,000,190.58
// Random number mean: 0.5001
//
// Task 6 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 999,720.21
// Random number mean: 0.4999
//
// Task 7 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 999,000.96
// Random number mean: 0.4995
//
// Task 8 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 999,499.33
// Random number mean: 0.4997
//
// Task 9 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 1,000,193.25
// Random number mean: 0.5001
//
// Task 10 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 999,960.82
// Random number mean: 0.5000
//
//
// Total random numbers generated: 22,000,000
// Total sum of all random numbers: 11,000,067.33
// Random number mean: 0.5000
open System
open System.Threading
open System.Threading.Tasks
type Example() =
static let source = new CancellationTokenSource()
static let rand = Random()
static let randLock = obj ()
static let numericLock = obj ()
let mutable totalValue = 0.0
let mutable totalCount = 0
member _.Execute() =
use source = source // Dispose of the CancellationTokenSource when we're done with it.
let token = source.Token
let tasks =
[| for i = 0 to 10 do
Task.Run(
(fun () ->
let mutable previous = 0.0
let mutable taskCtr = 0
let mutable taskTotal = 0.0
let mutable result = 0.0
for _ = 1 to 2000000 do
// Make sure there's no corruption of Random.
token.ThrowIfCancellationRequested()
lock randLock (fun () -> result <- rand.NextDouble())
// Check for corruption of Random instance.
if result = previous && result = 0.0 then
source.Cancel()
else
previous <- result
taskCtr <- taskCtr + 1
taskTotal <- taskTotal + result
lock numericLock (fun () ->
// Show result.
printfn "Task %i finished execution." i
printfn $"Random numbers generated: {taskCtr:N0}"
printfn $"Sum of random numbers: {taskTotal:N2}"
printfn $"Random number mean: {(taskTotal / float taskCtr):N4}\n"
// Update overall totals.
totalCount <- totalCount + taskCtr
totalValue <- totalValue + taskTotal)),
token
) |]
try
// Run tasks with F# Async.
Task.WhenAll tasks
|> Async.AwaitTask
|> Async.RunSynchronously
printfn $"\nTotal random numbers generated: {totalCount:N0}"
printfn $"Total sum of all random numbers: {totalValue:N2}"
printfn $"Random number mean: {(totalValue / float totalCount):N4}"
with
| :? AggregateException as e ->
for inner in e.InnerExceptions do
match inner with
| :? TaskCanceledException as canc ->
if canc <> null then
printfn $"Task #{canc.Task.Id} cancelled"
else
printfn $"Exception: {inner.GetType().Name}"
| _ -> ()
let ex = Example()
Thread.CurrentThread.Name <- "Main"
ex.Execute()
// The example displays output like the following:
// Task 1 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 1,000,502.47
// Random number mean: 0.5003
//
// Task 0 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 1,000,445.63
// Random number mean: 0.5002
//
// Task 2 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 1,000,556.04
// Random number mean: 0.5003
//
// Task 3 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 1,000,178.87
// Random number mean: 0.5001
//
// Task 4 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 999,819.17
// Random number mean: 0.4999
//
// Task 5 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 1,000,190.58
// Random number mean: 0.5001
//
// Task 6 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 999,720.21
// Random number mean: 0.4999
//
// Task 7 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 999,000.96
// Random number mean: 0.4995
//
// Task 8 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 999,499.33
// Random number mean: 0.4997
//
// Task 9 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 1,000,193.25
// Random number mean: 0.5001
//
// Task 10 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 999,960.82
// Random number mean: 0.5000
//
//
// Total random numbers generated: 22,000,000
// Total sum of all random numbers: 11,000,067.33
// Random number mean: 0.5000
Imports System.Collections.Generic
Imports System.Threading
Imports System.Threading.Tasks
Module Example16
Dim source As New CancellationTokenSource()
Dim randLock As New Object()
Dim numericLock As New Object()
Dim rand As New Random()
Dim totalValue As Double = 0.0
Dim totalCount As Integer = 0
Public Sub Main()
Dim tasks As New List(Of Task)()
For ctr As Integer = 1 To 10
Dim token As CancellationToken = source.Token
Dim taskNo As Integer = ctr
tasks.Add(Task.Run(
Sub()
Dim previous As Double = 0.0
Dim taskCtr As Integer = 0
Dim taskTotal As Double = 0.0
Dim result As Double = 0.0
For n As Integer = 1 To 2000000
' Make sure there's no corruption of Random.
token.ThrowIfCancellationRequested()
SyncLock randLock
result = rand.NextDouble()
End SyncLock
' Check for corruption of Random instance.
If result = previous AndAlso result = 0 Then
source.Cancel()
Else
previous = result
End If
taskCtr += 1
taskTotal += result
Next
' Show result.
Console.WriteLine("Task {0} finished execution.", taskNo)
Console.WriteLine("Random numbers generated: {0:N0}", taskCtr)
Console.WriteLine("Sum of random numbers: {0:N2}", taskTotal)
Console.WriteLine("Random number mean: {0:N4}", taskTotal / taskCtr)
Console.WriteLine()
' Update overall totals.
SyncLock numericLock
totalCount += taskCtr
totalValue += taskTotal
End SyncLock
End Sub, token))
Next
Try
Task.WaitAll(tasks.ToArray())
Console.WriteLine()
Console.WriteLine("Total random numbers generated: {0:N0}", totalCount)
Console.WriteLine("Total sum of all random numbers: {0:N2}", totalValue)
Console.WriteLine("Random number mean: {0:N4}", totalValue / totalCount)
Catch e As AggregateException
For Each inner As Exception In e.InnerExceptions
Dim canc As TaskCanceledException = TryCast(inner, TaskCanceledException)
If canc IsNot Nothing Then
Console.WriteLine("Task #{0} cancelled.", canc.Task.Id)
Else
Console.WriteLine("Exception: {0}", inner.GetType().Name)
End If
Next
Finally
source.Dispose()
End Try
End Sub
End Module
' The example displays output like the following:
' Task 1 finished execution.
' Random numbers generated: 2,000,000
' Sum of random numbers: 1,000,502.47
' Random number mean: 0.5003
'
' Task 0 finished execution.
' Random numbers generated: 2,000,000
' Sum of random numbers: 1,000,445.63
' Random number mean: 0.5002
'
' Task 2 finished execution.
' Random numbers generated: 2,000,000
' Sum of random numbers: 1,000,556.04
' Random number mean: 0.5003
'
' Task 3 finished execution.
' Random numbers generated: 2,000,000
' Sum of random numbers: 1,000,178.87
' Random number mean: 0.5001
'
' Task 4 finished execution.
' Random numbers generated: 2,000,000
' Sum of random numbers: 999,819.17
' Random number mean: 0.4999
'
' Task 5 finished execution.
' Random numbers generated: 2,000,000
' Sum of random numbers: 1,000,190.58
' Random number mean: 0.5001
'
' Task 6 finished execution.
' Random numbers generated: 2,000,000
' Sum of random numbers: 999,720.21
' Random number mean: 0.4999
'
' Task 7 finished execution.
' Random numbers generated: 2,000,000
' Sum of random numbers: 999,000.96
' Random number mean: 0.4995
'
' Task 8 finished execution.
' Random numbers generated: 2,000,000
' Sum of random numbers: 999,499.33
' Random number mean: 0.4997
'
' Task 9 finished execution.
' Random numbers generated: 2,000,000
' Sum of random numbers: 1,000,193.25
' Random number mean: 0.5001
'
' Task 10 finished execution.
' Random numbers generated: 2,000,000
' Sum of random numbers: 999,960.82
' Random number mean: 0.5000
'
'
' Total random numbers generated: 22,000,000
' Total sum of all random numbers: 11,000,067.33
' Random number mean: 0.5000
Il diffère du premier exemple de la manière suivante :
- Les variables permettant de suivre le nombre de nombres aléatoires générés et leur somme dans chaque tâche sont locales pour la tâche. Il n’est donc pas nécessaire d’utiliser l’attribut ThreadStaticAttribute .
- La méthode statique Task.WaitAll est utilisée pour s’assurer que le thread principal ne se termine pas avant que toutes les tâches aient terminé. Il n’y a pas besoin de l’objet CountdownEvent .
- L’exception qui résulte de l’annulation de tâche est exposée dans la Task.WaitAll méthode. Dans l’exemple précédent, il est géré par chaque thread.
Générer différents types de nombres aléatoires
Le générateur de nombres aléatoires fournit des méthodes qui vous permettent de générer les types de nombres aléatoires suivants :
Série de Byte valeurs. Vous déterminez le nombre de valeurs d’octets en passant un tableau initialisé au nombre d’éléments que vous souhaitez que la méthode retourne à la NextBytes méthode. L’exemple suivant génère 20 octets.
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
Entier unique. Vous pouvez choisir si vous souhaitez un entier compris entre 0 et une valeur maximale (Int32.MaxValue - 1) en appelant la Next() méthode, un entier compris entre 0 et une valeur spécifique en appelant la Next(Int32) méthode, ou un entier dans une plage de valeurs en appelant la Next(Int32, Int32) méthode. Dans les surcharges paramétrables, la valeur maximale spécifiée est exclusive ; autrement dit, le nombre maximal réel généré est un nombre inférieur à la valeur spécifiée.
L’exemple suivant appelle la Next(Int32, Int32) méthode pour générer 10 nombres aléatoires compris entre -10 et 10. Notez que le deuxième argument de la méthode spécifie la limite supérieure exclusive de la plage de valeurs aléatoires retournées par la méthode. En d’autres termes, le plus grand entier que la méthode peut retourner est inférieur à cette valeur.
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
Valeur à virgule flottante unique comprise entre 0,0 et moins de 1,0 en appelant la NextDouble méthode. La limite supérieure exclusive du nombre aléatoire retourné par la méthode est 1. Sa limite supérieure réelle est donc 0,9999999999999999999999978. L’exemple suivant génère 10 nombres à virgule flottante aléatoire.
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
Important
La Next(Int32, Int32) méthode vous permet de spécifier la plage du nombre aléatoire retourné. Toutefois, le maxValue
paramètre, qui spécifie le nombre retourné dans la plage supérieure, est une valeur exclusive et non inclusive. Cela signifie que l’appel Next(0, 100)
de méthode retourne une valeur comprise entre 0 et 99, et non entre 0 et 100.
Vous pouvez également utiliser la Random classe pour des tâches telles que la génération de valeurs booléennes aléatoires, la génération de valeurs à virgule flottante aléatoire dans une plage spécifiée, la génération d’entiers 64 bits aléatoires et la récupération d’un élément unique à partir d’un tableau ou d’une collection.
Remplacez votre propre algorithme
Vous pouvez implémenter votre propre générateur de nombres aléatoires en hériter de la Random classe et en fournissant votre algorithme de génération de nombres aléatoires. Pour fournir votre propre algorithme, vous devez remplacer la Sample méthode, qui implémente l’algorithme de génération de nombre aléatoire. Vous devez également remplacer les méthodes et NextBytes les Next()méthodes Next(Int32, Int32)pour s’assurer qu’elles appellent votre méthode substituéeSample. Vous n’avez pas besoin de remplacer les méthodes et NextDouble les Next(Int32) méthodes.
Pour obtenir un exemple qui dérive de la Random classe et modifie son générateur de nombres pseudo-aléatoires par défaut, consultez la Sample page de référence.
Récupérer la même séquence de valeurs aléatoires
Parfois, vous souhaitez générer la même séquence de nombres aléatoires dans les scénarios de test logiciel et dans le jeu. Le test avec la même séquence de nombres aléatoires vous permet de détecter les régressions et de confirmer les correctifs de bogues. L’utilisation de la même séquence de nombres aléatoires dans les jeux vous permet de relire les jeux précédents.
Vous pouvez générer la même séquence de nombres aléatoires en fournissant la même valeur initiale au Random(Int32) constructeur. La valeur initiale fournit une valeur de départ pour l’algorithme de génération de nombre pseudo-aléatoire. L’exemple suivant utilise 100100 comme valeur initiale arbitraire pour instancier l’objet Random , affiche 20 valeurs à virgule flottante aléatoire et conserve la valeur initiale. Il restaure ensuite la valeur initiale, instancie un nouveau générateur de nombres aléatoires et affiche les mêmes 20 valeurs à virgule flottante aléatoire. Notez que l’exemple peut produire différentes séquences de nombres aléatoires si elles sont exécutées sur différentes versions de .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
Récupérer des séquences uniques de nombres aléatoires
La fourniture de valeurs initiales différentes aux instances de la Random classe entraîne la production d’une séquence différente de valeurs par chaque générateur de nombres aléatoires. Vous pouvez fournir une valeur initiale explicitement en appelant le Random(Int32) constructeur, ou implicitement en appelant le Random() constructeur. La plupart des développeurs appellent le constructeur sans paramètre, qui utilise l’horloge système. L’exemple suivant utilise cette approche pour instancier deux Random instances. Chaque instance affiche une série de 10 entiers aléatoires.
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
Toutefois, en raison de sa résolution finie, l’horloge système ne détecte pas les différences de temps inférieures à environ 15 millisecondes. Par conséquent, si votre code appelle la Random() surcharge sur .NET Framework pour instancier deux Random objets en succession, vous pouvez fournir par inadvertance les objets avec des valeurs initiales identiques. (La Random classe dans .NET Core n’a pas cette limitation.) Pour voir cela dans l’exemple précédent, commentez l’appel Thread.Sleep de méthode, puis compilez et réexécutez l’exemple.
Pour éviter cela, nous vous recommandons d’instancier un seul Random objet plutôt que plusieurs. Toutefois, comme Random n’est pas thread sécurisé, vous devez utiliser un appareil de synchronisation si vous accédez à une Random instance à partir de plusieurs threads. Pour plus d’informations, consultez la section Sécurité des threads. Vous pouvez également utiliser un mécanisme de délai, tel que la Sleep méthode utilisée dans l’exemple précédent, pour vous assurer que les instanciations se produisent plus de 15 millisecondes.
Récupérer des entiers dans une plage spécifiée
Vous pouvez récupérer des entiers dans une plage spécifiée en appelant la Next(Int32, Int32) méthode, ce qui vous permet de spécifier à la fois la limite inférieure et supérieure des nombres que vous souhaitez que le générateur de nombres aléatoires retourne. La limite supérieure est une valeur exclusive, et non inclusive. Autrement dit, elle n’est pas incluse dans la plage de valeurs retournées par la méthode. L’exemple suivant utilise cette méthode pour générer des entiers aléatoires compris entre -10 et 10. Notez qu’il spécifie 11, qui est supérieur à la valeur souhaitée, comme valeur de l’argument dans l’appel maxValue
de méthode.
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
Récupérer des entiers avec un nombre spécifié de chiffres
Vous pouvez appeler la Next(Int32, Int32) méthode pour récupérer des numéros avec un nombre spécifié de chiffres. Par exemple, pour récupérer des nombres avec quatre chiffres (c’est-à-dire des nombres compris entre 1000 et 9999), vous appelez la Next(Int32, Int32) méthode avec une minValue
valeur de 1000 et une maxValue
valeur de 1 0000, comme l’illustre l’exemple suivant.
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
Récupérer des valeurs à virgule flottante dans une plage spécifiée
La NextDouble méthode retourne des valeurs à virgule flottante aléatoire comprises entre 0 et moins de 1. Toutefois, vous souhaiterez souvent générer des valeurs aléatoires dans une autre plage.
Si l’intervalle entre les valeurs minimales et maximales souhaitées est de 1, vous pouvez ajouter la différence entre l’intervalle de départ souhaité et 0 au nombre retourné par la NextDouble méthode. L’exemple suivant montre comment générer 10 nombres aléatoires compris entre -1 et 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
Pour générer des nombres à virgule flottante aléatoire dont la limite inférieure est 0, mais la limite supérieure est supérieure à 1 (ou, dans le cas de nombres négatifs, dont la limite inférieure est inférieure à -1 et la limite supérieure est 0), multipliez le nombre aléatoire par la limite non nulle. L’exemple suivant montre comment générer 20 millions de nombres à virgule flottante aléatoire compris entre 0 et Int64.MaxValue. En outre, affiche la distribution des valeurs aléatoires générées par la méthode.
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 %
Pour générer des nombres à virgule flottante aléatoire entre deux valeurs arbitraires, comme la Next(Int32, Int32) méthode pour les entiers, utilisez la formule suivante :
Random.NextDouble() * (maxValue - minValue) + minValue
L’exemple suivant génère 1 million de nombres aléatoires compris entre 10,0 et 11,0 et affiche leur distribution.
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 %)
Générer des valeurs booléennes aléatoires
La Random classe ne fournit pas de méthodes qui génèrent des Boolean valeurs. Toutefois, vous pouvez définir votre propre classe ou méthode pour ce faire. L’exemple suivant définit une classe, BooleanGenerator
avec une seule méthode. NextBoolean
La BooleanGenerator
classe stocke un Random objet en tant que variable privée. La NextBoolean
méthode appelle la Random.Next(Int32, Int32) méthode et transmet le résultat à la Convert.ToBoolean(Int32) méthode. Notez que 2 est utilisé comme argument pour spécifier la limite supérieure du nombre aléatoire. Étant donné qu’il s’agit d’une valeur exclusive, l’appel de méthode retourne 0 ou 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 %)
Au lieu de créer une classe distincte pour générer des valeurs aléatoires Boolean , l’exemple peut simplement avoir défini une seule méthode. Dans ce cas, toutefois, l’objet doit avoir été défini comme une variable au niveau de la Random classe pour éviter d’instancier une nouvelle Random instance dans chaque appel de méthode. Dans Visual Basic, l’instance aléatoire peut être définie en tant que variable statique dans la NextBoolean
méthode. L’exemple suivant fournit une implémentation.
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 %)
Générer des entiers 64 bits aléatoires
Les surcharges de la Next méthode retournent des entiers 32 bits. Toutefois, dans certains cas, vous pouvez utiliser des entiers 64 bits. Vous pouvez procéder comme suit :
Appelez la NextDouble méthode pour récupérer une valeur à virgule flottante double précision.
Multipliez cette valeur par Int64.MaxValue.
L’exemple suivant utilise cette technique pour générer 20 millions d’entiers longs aléatoires et les classer dans 10 groupes égaux. Il évalue ensuite la distribution des nombres aléatoires en comptant le nombre dans chaque groupe de 0 à Int64.MaxValue. Comme le montre la sortie de l’exemple, les nombres sont distribués plus ou moins de manière égale à travers la plage d’un entier long.
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 %
Une autre technique qui utilise la manipulation de bits ne génère pas de nombres vraiment aléatoires. Cette technique appelle Next() pour générer deux entiers, les décalages gauches un par 32 bits et les OR ensemble. Cette technique présente deux limitations :
Étant donné que le bit 31 est le bit de signe, la valeur en bit 31 de l’entier long résultant est toujours 0. Cela peut être résolu en générant un nombre aléatoire de 0 ou 1, en le déplaçant à gauche de 31 bits et en oRing avec l’entier long aléatoire d’origine.
Plus sérieusement, étant donné que la probabilité que la valeur retournée par Next() sera 0, il n’y aura que peu de nombres aléatoires dans la plage 0x0-0x00000000FFFFFFFF.
Récupérer des octets dans une plage spécifiée
Les surcharges de la Next méthode vous permettent de spécifier la plage de nombres aléatoires, mais pas la NextBytes méthode. L’exemple suivant implémente une NextBytes
méthode qui vous permet de spécifier la plage des octets retournés. Il définit une Random2
classe qui dérive de Random et surcharge sa NextBytes
méthode.
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
La NextBytes(Byte[], Byte, Byte)
méthode encapsule un appel à la Next(Int32, Int32) méthode et spécifie la valeur minimale et une valeur supérieure à la valeur maximale (dans ce cas, 0 et 101) que nous voulons retourner dans le tableau d’octets. Étant donné que nous sommes sûrs que les valeurs entières retournées par la Next méthode se trouvent dans la plage du type de Byte données, nous pouvons les convertir en toute sécurité (en C# et F#) ou les convertir (en Visual Basic) à partir d’entiers en octets.
Récupérer un élément à partir d’un tableau ou d’une collection au hasard
Les nombres aléatoires servent souvent d’index pour récupérer des valeurs à partir de tableaux ou de collections. Pour récupérer une valeur d’index aléatoire, vous pouvez appeler la Next(Int32, Int32) méthode et utiliser la limite inférieure du tableau comme valeur de son minValue
argument et une valeur supérieure à la limite supérieure du tableau comme valeur de son maxValue
argument. Pour un tableau de base zéro, cela équivaut à sa Length propriété ou à une valeur supérieure à la valeur retournée par la Array.GetUpperBound méthode. L’exemple suivant récupère de façon aléatoire le nom d’une ville dans le États-Unis à partir d’un tableau de villes.
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
Récupérer un élément unique à partir d’un tableau ou d’une collection
Un générateur de nombres aléatoires peut toujours retourner des valeurs en double. À mesure que la plage de nombres devient plus petite ou que le nombre de valeurs générées devient plus grand, la probabilité de doublons augmente. Si les valeurs aléatoires doivent être uniques, davantage de nombres sont générés pour compenser les doublons, ce qui entraîne de plus en plus de mauvaises performances.
Il existe plusieurs techniques pour gérer ce scénario. Une solution courante consiste à créer un tableau ou une collection qui contient les valeurs à récupérer et un tableau parallèle qui contient des nombres à virgule flottante aléatoire. Le deuxième tableau est rempli avec des nombres aléatoires au moment de la création du premier tableau et la Array.Sort(Array, Array) méthode est utilisée pour trier le premier tableau à l’aide des valeurs du tableau parallèle.
Par exemple, si vous développez un jeu Solitaire, vous souhaitez vous assurer que chaque carte n’est utilisée qu’une seule fois. Au lieu de générer des nombres aléatoires pour récupérer une carte et suivre si cette carte a déjà été traitée, vous pouvez créer un tableau parallèle de nombres aléatoires qui peuvent être utilisés pour trier le jeu. Une fois le jeu trié, votre application peut conserver un pointeur pour indiquer l’index de la carte suivante sur le jeu.
L'exemple suivant illustre cette approche. Il définit une classe qui représente une Card
carte de jeu et une Dealer
classe qui traite un jeu de cartes aléatoires. Le Dealer
constructeur de classe remplit deux tableaux : un tableau qui a une deck
étendue de classe et qui représente toutes les cartes du jeu ; et un tableau local order
qui a le même nombre d’éléments que le deck
tableau et est rempli avec des valeurs générées Double de manière aléatoire. La Array.Sort(Array, Array) méthode est ensuite appelée pour trier le deck
tableau en fonction des valeurs du order
tableau.
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