random-bit-generation
pseudo-randomness
probability-control
algorithm-design
computer-science

Fast way to generate pseudo-random bits with a given probability of 0 or 1 for each bit

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Generating pseudo-random bits with a controlled probability is the biased-coin version of ordinary random bit generation. The simplest method is comparing uniform random numbers against a threshold, but the best implementation depends on whether you need one bit, a large vector, or a packed bitstream. Performance usually comes from vectorization and batching, not from exotic probability math.

Use Thresholding on Uniform Random Values

If p is the probability of generating bit 1, then draw a uniform value in the interval from zero to one and compare it to p.

Simple Python example:

python
1import random
2
3
4def random_bit(p_one: float) -> int:
5    return 1 if random.random() < p_one else 0
6
7
8print([random_bit(0.7) for _ in range(10)])

This is conceptually correct and often good enough for small workloads.

Vectorize for Speed with NumPy

For large bit arrays, Python loops are the main bottleneck. NumPy vectorization is much faster.

python
1import numpy as np
2
3
4def random_bits(n: int, p_one: float) -> np.ndarray:
5    return (np.random.random(n) < p_one).astype(np.uint8)
6
7
8bits = random_bits(20, 0.3)
9print(bits)
10print(bits.mean())

This produces a vector of zero and one values, and the sample mean should approach p_one as n grows.

Pack Bits When Memory Matters

If you need millions of bits and memory footprint matters, keep them packed instead of storing one byte per bit.

python
1bits = random_bits(64, 0.4)
2packed = np.packbits(bits)
3print(bits)
4print(packed)

Packed storage is especially useful in simulations, compressed probabilistic models, and network-oriented workloads.

Generate Bits with Probability of Zero Instead

Some APIs define the probability of zero rather than one. The transformation is simple:

  • 'P(1) = p'
  • 'P(0) = 1 - p'

If the caller provides p_zero, convert it:

python
def random_bits_from_p_zero(n: int, p_zero: float) -> np.ndarray:
    return random_bits(n, 1.0 - p_zero)

Keep one internal convention to avoid subtle inversion bugs.

Validate the Distribution

Whenever this logic matters, verify the empirical distribution.

python
bits = random_bits(1_000_000, 0.25)
print(bits.mean())

For one million trials, the result should be close to 0.25, though never exactly equal. This is the right way to confirm the generator behavior.

Avoid Per-Bit Heavyweight RNG Setup

The fast path is:

  1. initialize the PRNG once
  2. generate many random values in one call
  3. threshold them in a batch

The slow path is:

  • repeated setup
  • Python-level branching for every bit
  • fragmented small allocations

That is why NumPy and other vectorized libraries dominate pure Python loops for this problem.

Use the Right Generator for the Job

For simulation or sampling, standard pseudo-random generators are usually fine. For security-sensitive tokens or adversarial settings, use a cryptographically secure source instead.

For example, Python secrets is designed for unpredictability, but it is slower and less suited to massive vectorized generation. Do not confuse statistical bias control with cryptographic strength.

Integer-Threshold Variant

If floating-point comparison is undesirable, you can compare against an integer threshold from a fixed range.

python
1import random
2
3
4def random_bit_int_threshold(p_one: float) -> int:
5    threshold = int(p_one * 1_000_000)
6    return 1 if random.randrange(1_000_000) < threshold else 0

This can be useful in systems where integer arithmetic is preferred, though it is not usually faster in high-level Python.

Common Pitfalls

  • Using Python loops for huge bit arrays when vectorization is available.
  • Confusing probability of one with probability of zero.
  • Expecting short samples to match the exact target probability closely.
  • Using non-cryptographic PRNGs in security-sensitive contexts.
  • Forgetting to validate distribution empirically after implementation changes.

Summary

  • The standard solution is thresholding uniform random values.
  • NumPy vectorization is the fastest common approach for large batches.
  • Use packbits when memory density matters.
  • Keep a single internal probability convention to avoid inversion errors.
  • Choose the random source based on whether you need simulation speed or cryptographic strength.

Course illustration
Course illustration

All Rights Reserved.