Domanda

Vorrei usare RNGCryptoServiceProvider come fonte di numeri casuali. Dato che può solo emetterli come una matrice di valori byte, come posso convertirli in un valore doppio da 0 a 1 preservando l'uniformità dei risultati?

È stato utile?

Soluzione

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

Altri suggerimenti

Ecco come lo farei.

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;
}
  

Per coincidenza 9007199254740991/9007199254740992 è ~ = 0.99999999999999988897769753748436 che è ciò che il metodo Random.NextDouble restituirà come valore massimo (vedere https://msdn.microsoft.com/en-us/library/system .random.nextdouble (v = vs.110) aspx ).

In generale, la deviazione standard di una distribuzione uniforme continua è (max - min) / sqrt (12).

Con una dimensione del campione di 1000 ottengo in modo affidabile entro un margine di errore del 2%.

Con una dimensione del campione di 10000 sto ottenendo in modo affidabile un margine di errore dell'1%.

Ecco come ho verificato questi risultati.

[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);
  }
}

È possibile utilizzare il metodo BitConverter.ToDouble (...). Prende in una matrice di byte e restituirà un doppio. Esistono metodi corrispondenti per la maggior parte degli altri tipi primitivi, nonché un metodo per passare dalle primitive a una matrice di byte.

Usa BitConverter per convertire una sequenza di byte casuali in un doppio:

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);
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top