Generating random boolean values is a common challenge in programming tasks, especially when randomness is required for gaming or testing data.

With that said, let’s explore some methods to generate boolean values as quickly as possible.

To download the source code for this article, you can visit our GitHub repository.

Random Generators

In our previous discussion on the Random class in C#, covered in the article titled “Random Class in C#“, we explored some different random generators available in .NET. We identified two main types: the pseudo-random generator class, Random, and the cryptographically secure generator class, RandomNumberGenerator.

Support Code Maze on Patreon to get rid of ads and get the best discounts on our products!
Become a patron at Patreon!

For most everyday programming tasks, we rely on the Random class. However, when dealing with security or cryptographic requirements, using the appropriate random generator, usually RandomNumberGenerator, is crucial. We have explored the RandomNumberGenerator in greater detail in our Create Cryptographic Numbers With RandomNumberGenerator article.

While cryptographically secure randomness might not be necessary in every scenario, it’s advisable to prioritize security whenever possible. However, it’s worth noting that RandomNumberGenerator tends to be significantly slower than the Random generator.

Random Classes for Our Testing Purposes

In .NET, random generators don’t offer a specific method for generating random boolean values. However, they do provide a general-purpose method for generating an array of various values, including booleans.

We’ll demonstrate creating bool values from int, long, double, byte, and boolean array. Let’s define the IRandomGenerator interface before proceeding:

public interface IRandomGenerator
{
    int NextInteger(int fromInclusive, int toExclusive);

    long NextLong(long fromInclusive, long toExclusive);

    double NextDouble();

    void GetItems<T>(ReadOnlySpan<T> choices, Span<T> destination);
}

This interface will encompass all the methods required for various algorithms to generate random boolean values.

Next, we need to create a concrete implementation of this interface. We do that using the RandomNumberGenerator and Random classes, and the implementation can be found by clicking on both links.

The results are clear and unmistakable:

| Method                      | NumberOfIntegers | Mean          |
|---------------------------- |----------------- |--------------:|
| SystemRandomGenerator       | 1000             |      2.017 us |
| CryptographyRandomGenerator | 1000             |     46.283 us |
| SystemRandomGenerator       | 1000000          |  1,957.859 us |
| CryptographyRandomGenerator | 1000000          | 45,801.955 us |

RandomNumberGenerator proves to be more than 20 times slower in heavy usage scenarios. Therefore, if cryptographic precision isn’t necessary for our task, it’s unsuitable for our purposes.

Generating Random Booleans

By measuring the speed of .NET built-in random generators, we’ve determined that the Random class offers the best performance in terms of speed.

Implementing the same interface is advantageous for consistency and ease of comparison. Different classes will implement different algorithms, but they will implement the same interface:

internal interface IBooleanGenerator
{
    bool NextBool();
}

Generating a Random Boolean Using Integers

The concept is straightforward. We’ll generate an int, either 0 or 1, and if it’s 0, we’ll return true; otherwise, we’ll return false:

public class NextIntegerGenerator(IRandomGenerator randomGenerator) : IBooleanGenerator
{
    public bool NextBool() => randomGenerator.NextInteger(0, 2) == 0;
}

This implementation of the IBooleanGenerator interface takes one of the random generators we discussed earlier as a parameter.

The NextBool() method calls the underlying generator to produce an int, either 0 or 1, and from that, it constructs the bool value.

Generating a Random Boolean Using Doubles

We can apply a similar concept to floating-point numbers. The original generator always produces numbers between 0.0 and 1.0. Therefore, we can return true if the generated number is greater than 0.5:

public class NextDoubleGenerator(IRandomGenerator randomGenerator) : IBooleanGenerator
{
    public bool NextBool() => randomGenerator.NextDouble() > 0.5;
}

We are again implementing the IBooleanGenerator interface which accepts a random generator that implements the IRandomGenerator interface.

In this case, only the SystemRandomGenerator is suitable, as the CryptographyRandomGenerator does not implement the NextDouble() method.

Generating a Random Boolean Using GetItems

Our next method in the toolbox is the GetItems() method, capable of generating an array of various types of elements. Since it’s a generic method, it can also generate bool values:

public class GetItemsGenerator(IRandomGenerator randomGenerator) : IBooleanGenerator
{
    public bool NextBool()
    {
        Span<bool> destination = stackalloc bool[1];
        randomGenerator.GetItems([false, true], destination);

        return destination[0];
    }
}

The structure of the GetItemsGenerator class mirrors that of the previous NextDoubleGenerator and NextIntegerGenerator. However, this time, we utilize the GetItems() method.

The GetItems() accepts a ReadOnlySpan of possible values and then fills a Span with randomly selected items from it.

To generate bool values, we provide an array containing all possible boolean values [true, false], along with a destination Span with length 1. We use a stackalloc array to minimize memory allocations. 

Base Benchmarking Methods

We will use the Random generator because it is faster than the RandomNumberGenerator generator. Also, we have prepared two helper methods for the benchmark, which you can find inside the BenchmarkBase.

The BenchmarkBase class will serve as the foundation for all our benchmarking activities. 

We’ve implemented several algorithms and set up the foundational benchmarking methods. Now, it’s time to measure our initial results. We’ll test the creation of one thousand and one million random booleans using the SystemRandomGenerator and various algorithms:

| Method               | NumberOfBooleans | Mean          | Allocated |
|--------------------- |----------------- |--------------:|----------:|
| NextIntegerGenerator | 1000             |      6.387 us |      24 B |
| NextDoubleGenerator  | 1000             |      7.847 us |      24 B |
| GetItemsGenerator    | 1000             |     15.948 us |      24 B |
| NextIntegerGenerator | 1000000          |  6,247.900 us |      72 B |
| NextDoubleGenerator  | 1000000          |  7,839.197 us |     120 B |
| GetItemsGenerator    | 1000000          | 15,467.424 us |      36 B |

The results of the NextIntegerGenerator and NextDoubleGenerator are pretty similar, but the performance of the GetItemsGenerator is notably worse.

This outcome is expected, considering that the GetItemsGenerator populates one million arrays with one boolean to create one million booleans, whereas the other generators accomplish this more directly.

Let’s investigate if using the GetItems() method for generating larger arrays at once makes any difference in performance.

GetItems() Method Can Already Generate an Array of Booleans

Let’s be fair in evaluating the performance of the GetItems() method by measuring how this benchmark compares with the others.

Instead of generating one million arrays with one boolean each, we’ll generate a single array with one million booleans. This approach will provide a more accurate representation of the GetItems() method’s performance. This time, we’ll need to store all the results since the GetItems() method returns the entire array, and the other methods must do the same.

Let’s compare the performance of this new method with the rest of them:

| Method                  | NumberOfBooleans | Mean            | Allocated |
|------------------------ |----------------- |----------------:|----------:|
| GetItemsDirectGenerator | 1000             |        171.8 ns |      96 B |
| NextIntegerGenerator    | 1000             |      2,025.5 ns |      24 B |
| NextDoubleGenerator     | 1000             |      2,351.9 ns |      24 B |
| GetItemsGenerator       | 1000             |     10,895.0 ns |      24 B |
| GetItemsDirectGenerator | 1000000          |     88,762.3 ns |      97 B |
| NextIntegerGenerator    | 1000000          |  1,980,366.4 ns |      26 B |
| NextDoubleGenerator     | 1000000          |  2,365,063.9 ns |      26 B |
| GetItemsGenerator       | 1000000          | 10,470,485.6 ns |     120 B |

This time, the direct call to the GetItems() method yields results similar to the NextIntegerGenerator.

However, it’s crucial to note that a direct call to GetItems() doesn’t respect our IBooleanGenerator interface. Our objective isn’t to generate an array of booleans at once but rather to have a method that can quickly generate a single boolean when needed.

GetItems() Method With Buffer

If we want to use this generator, we can significantly enhance its speed using the internal buffer. To make it respect our IBooleanGenerator interface, we can internally have a small buffer of booleans and replace it whenever we run out of new booleans:

public class GetItemsWithBufferGenerator(IRandomGenerator randomGenerator, int bufferLength) : IBooleanGenerator
{
    private static readonly bool[] _allPossibilities = [false, true];
    private readonly bool[] _buffer = new bool[bufferLength];

    private int _currentBufferIndex = bufferLength;

    public bool NextBool()
    {
        if (_currentBufferIndex >= _buffer.Length)
        {
            randomGenerator.GetItems<bool>(_allPossibilities, _buffer);
            _currentBufferIndex = 0;
        }

        return _buffer[_currentBufferIndex++];
    }
}

With that enhancement and the buffer of 128 elements, the code is still not as fast as a simple NextIntegerGenerator():

| Method                      | NumberOfBooleans | Mean         | Allocated |
|---------------------------- |----------------- |-------------:|----------:|
| NextIntegerGenerator        | 1000             |     6.416 us |      24 B |
| GetItemsWithBufferGenerator | 1000             |     8.895 us |     192 B |
| NextIntegerGenerator        | 1000000          | 6,324.178 us |      72 B |
| GetItemsWithBufferGenerator | 1000000          | 8,450.456 us |     288 B |

However, we can explore an alternative approach. We’ve observed that generating a single int, byte, double, or any other type is the primary bottleneck in our algorithms.

Generate a Random Bool Using a Single Bit

Currently, our fastest method involves translating the int result into a bool. However, we’re generating an integer on each iteration. But an int can have 4,294,967,296 different values! And we translate this into two values (true/false). This appears to be a significant waste of resources.

Since int in .NET is four bytes long, equivalent to 32 bits, and each bit can represent either zero or one (true or false), we can leverage this. We can direct the underlying random generator to produce an integer within the range from int.MinValue to int.MaxValue and then extract 32 random booleans from it.

Let’s test if this approach yields better performance:

public class NextIntegerBitsGenerator(IRandomGenerator randomGenerator) : IBooleanGenerator
{
    private const int BitsInByte = 8;
    private const int MaxBitIndex = sizeof(int) * BitsInByte;

    private int _randomInteger;
    private int _currentBufferIndex = MaxBitIndex;

    public bool NextBool()
    {
        if (_currentBufferIndex >= MaxBitIndex)
        {
            _randomInteger = randomGenerator.NextInteger(int.MinValue, int.MaxValue);
            _currentBufferIndex = 0;
        }

        return (_randomInteger & (1 << _currentBufferIndex++)) != 0;
    }
}

We begin by determining the number of bits in a byte (BitsInByte) and the total number of bits in a single int (MaxBitIndex).

Next, we maintain two variables: _randomInteger to store the current random integer and _currentBufferIndex to track the current index we’re on while traversing the integer bits.

In the algorithm itself, we check the bit at the current index and return true if it’s set or false if it’s not set. If we exhaust all the bits in the int, we generate a new random int and reset the index to zero.

Now, the moment of truth: is this algorithm faster than the current fastest one? Let’s benchmark them:

| Method                   | NumberOfBooleans | Mean         | Allocated |
|------------------------- |----------------- |-------------:|----------:|
| NextIntegerBitsGenerator | 1000             |     4.219 us |      32 B |
| NextIntegerGenerator     | 1000             |     6.428 us |      24 B |
| NextIntegerBitsGenerator | 1000000          | 4,303.753 us |      35 B |
| NextIntegerGenerator     | 1000000          | 6,223.402 us |      72 B |

The results are clear: we have a new winner, NextIntegerBitsGenerator().

Bits Representation, Little Endian vs. Big Endian.

We might question whether this algorithm is correct when considering how int values are represented in computer memory. We know that there are two distinct representations of bytes in multibyte types in computers: Little Endian and Big Endian systems, each having a different approach to int representation. However, this is precisely why we want all possible negative and positive integers.

As all negative and positive integers together encompass any combination of bits across these bytes, we’re indifferent to which number these bits represent. We understand that all possible numbers will generate all possible combinations of bits.

To make this statement more straightforward, consider this: we don’t care if the combination of bits ‘0011’ represents the number 12 or 3.

However, it’s worth noting a slight inconsistency: the number int.MaxValue can never be generated; thus, this particular combination of bits will never occur. But remember, this is only one combination out of 4,294,967,296 possible combinations.

Generate a Random Boolean Using an Array of Bytes Instead of an Integer

As before, when we talked about GetItems() method, we can again find another method that we can use instead of NextInteger() and that is NextBytes() method. Similar to GetItems(), the method NextBytes() also returns an array of bytes, and we can then check the bits of each generated byte. 

But again, measuring this approach shows that it is not faster than NextIntegerBitsGenerator().

From Integer to Long

Using the bits of an int is currently the fastest option. But we can use the long type as well to generate random bool values:

public class NextLongBitsGenerator(IRandomGenerator randomGenerator) : IBooleanGenerator
{
    private const int BitsInByte = 8;
    private const int MaxBitIndex = sizeof(long) * BitsInByte;

    private long _randomLong;
    private int _currentBufferIndex = MaxBitIndex;

    public bool NextBool()
    {
        if (_currentBufferIndex >= MaxBitIndex)
        {
            _randomLong = randomGenerator.NextLong(long.MinValue, long.MaxValue);
            _currentBufferIndex = 0;
        }

        return (_randomLong & (1L << _currentBufferIndex++)) != 0;
    }
}

There is a very important detail here. When shifting bits, we have to use type long, and that is why there is a constant 1L and not just 1. Without this, if we use 1 we would shift an int instead of long, and the results would be wrong!

So, let’s check the result:

| Method                   | NumberOfBooleans | Mean         | Ratio | Allocated |
|------------------------- |----------------- |-------------:|------:|----------:|
| NextLongBitsGenerator    | 1000             |     4.064 us |  0.97 |      40 B |
| NextIntegerBitsGenerator | 1000             |     4.199 us |  1.00 |      32 B |
|                          |                  |              |       |           |
| NextLongBitsGenerator    | 1000000          | 4,067.125 us |  0.99 |      43 B |
| NextIntegerBitsGenerator | 1000000          | 4,101.540 us |  1.00 |      80 B |

If we are creating a lot of tests, sometimes one is faster, and sometimes the other is faster. These are pretty close.

Fairness of our Random Boolean Algorithms

Now that we have found the winner, it is also time to check if our algorithms are fair (producing consistently fair results).

Randomness is very tricky, and despite that it is unlikely, there is a possibility that if we throw a die ten times, we will get six at each throw. Even though this is theoretically possible, we would probably conclude that the die is unfair.

So, when generating ten million boolean values, we expect to generate around five million true values and five million false values. But why don’t we check:

                  Generator:    true /   False | From the middle
          GetItemsGenerator: 4998654 / 5001346 | 0,0002692
        NextDoubleGenerator: 5000626 / 4999374 | 0,0001252
       NextIntegerGenerator: 4999618 / 5000382 | 0,0000764
   NextIntegerBitsGenerator: 4996202 / 5003798 | 0,0007596
      NextLongBitsGenerator: 5000193 / 4999807 | 0,0000386

By running this test many times, we see that all of our generators are fair and produce almost the same amount of random boolean values.

Conclusion

In this article, we’ve explored various algorithms for generating booleans. For a significant period, the most straightforward algorithm, which involves selecting integer numbers 0 as true and 1 as false, was the fastest option.

However, we’ve also discovered faster algorithms that leverage single bits for generating random boolean values, offering increased efficiency.

It’s safe to say that we can always rely on the simplest possible algorithm. However, if we desire or require a bit more speed, we can opt for manipulating bits and utilize the NextIntegerBitsGenerator or NextLongBitsGenerator algorithm.

Liked it? Take a second to support Code Maze on Patreon and get the ad free reading experience!
Become a patron at Patreon!