Question

I'm trying to get a random bool in C# for a scientific project. The problem is that the chance of the bool being true must be around 10^-12 to 10^-14; the default random function only generates ints and a value between 0 and [u]int.max is far to small.

How can I generate a random boolean value with such a low chance of being true?

Était-ce utile?

La solution

EDIT

Random.NextDouble() will not give the expected results!

As Kyle suggests in the comments below, the implementation of Random.NextDouble() maps an int to the interval [0.0 , 1.0), effectively making my code equivalent to r.Next( 0, Int32.MaxValue ) == 0. Adding more zeroes to will not affect the probability of the result being false.

Use one of the other answers, or use this code to generate a ulong (range 0-18446744073709551615) from System.Random.

var r = new Random();
var bytes = new byte[8];
r.NextBytes(bytes);
ulong result = BitConverter.ToUInt64(bytes, 0);

Original answer

Warning: will not work as expected, do not use! Provided merely for context.


Use Random.NextDouble() instead: the result is a double between 0.0 and 1.0

var r = new Random();
var probablyFalseBool = r.NextDouble() < 0.0000000000001;

Vary the number of zeroes if necessary.

Autres conseils

Try combining more than one random selection:

if(rnd.Next(0,10000) == 0 && rnd.Next(0,10000) == 0 && rnd.Next(0,10000) == 0){
}

Not sure if the C# syntax is correct, but this enter the if block with a probability of 10^-12.

You can use the NextBytes method to get large random numbers.

For example, this will give you the probability of 2.842e-14 that the boolean is true:

Random r = new Random();
byte[] buffer = new byte[6]; // make an array with 48 bits
r.NextBytes(buffer);
buffer[0] &= 0xf8; // mask out three bits to make it 45
bool occured = buffer.Sum(b => b) == 0;

As Nathan suggests, you can use the Next method to specify the range of results.

You can then use the && operator to ensure it meets your precision.

// 1 in a million
var result = random.Next(0, 1000) == 0 && random.Next(0, 1000) == 0; 

You can then change them to give you your desired rates.

You can easily control the odds like this:

var result = rnd.Next(0,100) == 0;

This would make the odds of result being true 1 in 100. You can easily adjust the values to whatever odds you want.

Go multidimensional! A normal random number is in a single dimension (i.e. along a line). By multiplying two random numbers together you are using two dimensions and so on.

If you take 6 random integers between 1 and 100, then multiply them together the odds of getting the result 1 is 1 in 10^12. Same with four random integers between 1 and 1000, or 3 random integers between 1 and 10000.

If you want something like 30 in 10^12 then use two random numbers between 1 and 1,000,000 and you want one to be 1 and the other 30 or less.

If you want something x (<1,000,000) in 10^12 then use two random numbers between 1 and 1,000,000 and you want one to be 1 and the other x or less.

Instead of Round.Next you can use Round.NextDouble or Round.NextBytes to get a much wider range.

With NextBytes you can use a buffer of 4-5 bytes to get the range you need. Very roughly, you could do something like this:

var buffer=new byte[5];
rnd.NextBytes(buffer);
return buffer.All(b=>b>0);

You probably can make this faster by using a BitArray

With NextDouble you would need to check that the result is less than 1/10^12.

Generate two ints in a row representing

(target_value div int_max, target_value mod int_max)

Check the larger for being 0, the second for being less than desired_precison * int_max.

if you have doubt about introducing a bias, randomize the order of the two ints in the pair before testing.

This standalone code illustrates the technique (tested here):

using System.IO;
using System;

class Program
{
    static void Main(string[] args)
    {
        bool   flip, strike;
        int    eps, lo, hi;
        Random rnd;

        eps = Int32.Parse(args[0]); // whatever is appropriate

        rnd = new Random();
        lo = rnd.Next(Int32.MaxValue);
        hi = rnd.Next(Int32.MaxValue);
        flip = (rnd.Next() > 0.5); 
        if (flip) { int swap; swap = lo; lo = hi; hi = swap; }

        strike = (hi == 0) && (lo < eps);

        Console.Write("[hi lo] = ");
        Console.Write(hi);
        Console.WriteLine(lo);
        if (strike) {
            Console.WriteLine("strike");
        }
    }
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top