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?

Was it helpful?

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.

OTHER TIPS

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");
        }
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top