Génération de valeurs (pseudo) contraintes aléatoires de (U) Int64 et Decimal

Remarque: Par souci de brièveté, les éléments suivants ne feront pas la distinction entre caractère aléatoire et pseudo-aléatoire. De plus, dans ce contexte, contrainte signifie entre des valeurs min et max données )

La classe System.Random fournit une génération aléatoire d’entiers, de doubles et de tableaux d’octets. En utilisant Random.Next, on peut facilement générer des valeurs contraintes aléatoires de type Boolean, Char, (S) Octet, (U) Int16, (U) Int32. En utilisant Random.NextDouble() , on peut également générer des valeurs contraintes de types Double et Single (pour autant que je sache ce type). La génération aléatoire de chaînes (d’une longueur et d’un alphabet donnés) a également été abordée auparavant .

Considérez les types de données primitifs restants (à l’exclusion de Object): Decimal et (U) Int64. Leur génération aléatoire a également été abordée ( Decimal , (U) Int64 en utilisant Random.NextBytes() ), mais pas lorsque contraint. L’échantillonnage de rejet (en boucle jusqu’à ce que la valeur générée soit la plage souhaitée) pourrait théoriquement être utilisé, mais ce n’est évidemment pas une solution pratique. Normaliser NextDouble() ne fonctionnera pas car il ne contient pas assez de chiffres significatifs.

En bref, je demande la mise en œuvre appropriée des fonctions suivantes:

 long NextLong(long min, long max) long NextDecimal(decimal min, decimal max) 

Notez que, étant donné que System.DateTime est basé sur un ulong, la première fonction permettrait également la génération contrainte aléatoire de telles structures (similaire à ici , uniquement en ticks au lieu de minutes).

Supposons que vous savez comment générer N bits aléatoires. Cela se fait assez facilement en utilisant NextBytes ou des appels répétés à Random.Next avec les limites appropriées.

Pour générer un long / ulong dans la plage correcte, déterminez l’ampleur de la plage et le nombre de bits requirejs pour la représenter. Vous pouvez ensuite utiliser un échantillonnage de rejet qui rejettera au pire la moitié des valeurs générées (par exemple, si vous souhaitez une valeur comprise dans la plage [0, 128], ce qui signifie que vous générerez [0, 255] plusieurs fois). Si vous voulez une plage non nulle, calculez simplement la taille de la plage, générez une valeur aléatoire dans [0, taille) puis ajoutez la base.

Je crois que générer une décimale aléatoire est beaucoup plus difficile. Indépendamment de toute autre chose, vous devez spécifier la dissortingbution souhaitée.

Cela devrait le faire. Pour la décimale, j’ai utilisé l’approche initiale de Jon Skeet pour générer des decimal aléatoires (sans contrainte). Pendant long j’ai fourni une méthode pour produire des s long aléatoires non négatifs aléatoires qui sont ensuite utilisés pour créer la valeur a dans la plage aléatoire.

Notez que pour la decimal la dissortingbution résultante n’est pas une dissortingbution uniforme sur [minValue, maxValue] . Elle est simplement uniforme sur toutes les représentations binarys des décimales comsockets dans la plage [minValue, maxValue] . Je ne vois pas de solution facile à ce problème sans utiliser l’échantillonnage de rejet.

Pendant long la dissortingbution résultante est uniforme sur [minValue, maxValue) .

 static class RandomExtensions { static int NextInt32(this Random rg) { unchecked { int firstBits = rg.Next(0, 1 << 4) << 28; int lastBits = rg.Next(0, 1 << 28); return firstBits | lastBits; } } public static decimal NextDecimal(this Random rg) { bool sign = rg.Next(2) == 1; return rg.NextDecimal(sign); } static decimal NextDecimal(this Random rg, bool sign) { byte scale = (byte)rg.Next(29); return new decimal(rg.NextInt32(), rg.NextInt32(), rg.NextInt32(), sign, scale); } static decimal NextNonNegativeDecimal(this Random rg) { return rg.NextDecimal(false); } public static decimal NextDecimal(this Random rg, decimal maxValue) { return (rg.NextNonNegativeDecimal() / Decimal.MaxValue) * maxValue; ; } public static decimal NextDecimal(this Random rg, decimal minValue, decimal maxValue) { if (minValue >= maxValue) { throw new InvalidOperationException(); } decimal range = maxValue - minValue; return rg.NextDecimal(range) + minValue; } static long NextNonNegativeLong(this Random rg) { byte[] bytes = new byte[sizeof(long)]; rg.NextBytes(bytes); // ssortingp out the sign bit bytes[7] = (byte)(bytes[7] & 0x7f); return BitConverter.ToInt64(bytes, 0); } public static long NextLong(this Random rg, long maxValue) { return (long)((rg.NextNonNegativeLong() / (double)Int64.MaxValue) * maxValue); } public static long NextLong(this Random rg, long minValue, long maxValue) { if (minValue >= maxValue) { throw new InvalidOperationException(); } long range = maxValue - minValue; return rg.NextLong(range) + minValue; } } 

Je suis venu ici pour trouver un moyen de générer des valeurs 64 bits dans une plage arbitraire. Les autres réponses n’ont pas réussi à produire un nombre aléatoire lorsque certaines plages ont été spécifiées (par exemple long.MinValue à long.MaxValue). Voici ma version qui semble résoudre le problème:

 public static long NextInt64(this Random random, long minValue, long maxValue) { Contract.Requires(random != null); Contract.Requires(minValue <= maxValue); Contract.Ensures(Contract.Result() >= minValue && Contract.Result() < maxValue); return (long)(minValue + (random.NextUInt64() % ((decimal)maxValue - minValue))); } 

Il utilise les méthodes d'extension suivantes:

 public static ulong NextUInt64(this Random random) { Contract.Requires(random != null); return BitConverter.ToUInt64(random.NextBytes(8), 0); } public static byte[] NextBytes(this Random random, int byteCount) { Contract.Requires(random != null); Contract.Requires(byteCount > 0); Contract.Ensures(Contract.Result() != null && Contract.Result().Length == byteCount); var buffer = new byte[byteCount]; random.NextBytes(buffer); return buffer; } 

La dissortingbution n’est pas parfaite, même si la taille de la plage demandée n’est pas un diviseur pur de 2 ^ 64, mais elle fournit au moins un nombre aléatoire dans la plage de demandes pour une plage donnée.

Basé sur la méthode de Jon Skeet, voici mon essai:

 public static long NextLong(this Random rnd, long min, long max) { if (max <= min) { throw new Exception("Min must be less than max."); } long dif = max - min; var bytes = new byte[8]; rnd.NextBytes(bytes); bytes[7] &= 0x7f; //strip sign bit long posNum = BitConverter.ToInt64(bytes, 0); while (posNum > dif) { posNum >>= 1; } return min + posNum; } 

Faites-moi savoir si vous voyez des erreurs.

 long posNum = BitConverter.ToInt64(Guid.NewGuid().ToByteArray(), 0); use this instead of NextBytes