Comment obtenir une valeur double aléatoire à partir de valeurs de tableau d'octets aléatoires?

StackOverflow https://stackoverflow.com/questions/403572

  •  03-07-2019
  •  | 
  •  

Question

Je voudrais utiliser RNGCryptoServiceProvider comme source de nombres aléatoires. Comme il ne peut les afficher que sous forme de tableau de valeurs d'octets, comment puis-je les convertir en valeurs doubles de 0 à 1 tout en préservant l'uniformité des résultats?

Était-ce utile?

La solution

byte[] result = new byte[8];
rng.GetBytes(result);
return (double)BitConverter.ToUInt64(result,0) / ulong.MaxValue;

Autres conseils

Voici comment je le ferais.

private static readonly System.Security.Cryptography.RNGCryptoServiceProvider _secureRng;
public static double NextSecureDouble()
{
  var bytes = new byte[8];
  _secureRng.GetBytes(bytes);
  var v = BitConverter.ToUInt64(bytes, 0);
  // We only use the 53-bits of integer precision available in a IEEE 754 64-bit double.
  // The result is a fraction, 
  // r = (0, 9007199254740991) / 9007199254740992 where 0 <= r && r < 1.
  v &= ((1UL << 53) - 1);
  var r = (double)v / (double)(1UL << 53);
  return r;
}
  

Par coïncidence, 9007199254740991/9007199254740992 est ~ = 0.99999999999999999899769753748436 , qui correspond à la méthode Random.NextDouble , qui renvoie la valeur maximale (voir https://msdn.microsoft.com/en-us/library/system .random.nextdouble (v = vs.110) .aspx ).

En général, l'écart type d'une distribution uniforme continue est (max-min) / sqrt (12).

Avec une taille d'échantillon de 1 000, j'obtiens de manière fiable une marge d'erreur de 2%.

Avec une taille d'échantillon de 10 000, la marge d'erreur est inférieure à 1%.

Voici comment j'ai vérifié ces résultats.

[Test]
public void Randomness_SecureDoubleTest()
{
  RunTrials(1000, 0.02);
  RunTrials(10000, 0.01);
}

private static void RunTrials(int sampleSize, double errorMargin)
{
  var q = new Queue<double>();

  while (q.Count < sampleSize)
  {
    q.Enqueue(Randomness.NextSecureDouble());
  }

  for (int k = 0; k < 1000; k++)
  {
    // rotate
    q.Dequeue();
    q.Enqueue(Randomness.NextSecureDouble());

    var avg = q.Average();

    // Dividing by n−1 gives a better estimate of the population standard
    // deviation for the larger parent population than dividing by n, 
    // which gives a result which is correct for the sample only.

    var actual = Math.Sqrt(q.Sum(x => (x - avg) * (x - avg)) / (q.Count - 1));

    // see http://stats.stackexchange.com/a/1014/4576

    var expected = (q.Max() - q.Min()) / Math.Sqrt(12);

    Assert.AreEqual(expected, actual, errorMargin);
  }
}

Vous pouvez utiliser la méthode BitConverter.ToDouble (...). Il prend dans un tableau d'octets et retournera un double. Il existe des méthodes correspondantes pour la plupart des autres types de primitives, ainsi qu’une méthode permettant de passer des primitives à un tableau d’octets.

Utilisez BitConverter pour convertir une séquence d'octets aléatoires en un double:

byte[] random_bytes = new byte[8];  // BitConverter will expect an 8-byte array
new RNGCryptoServiceProvider().GetBytes(random_bytes);

double my_random_double = BitConverter.ToDouble(random_bytes, 0);
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top