Skip to content

Random Numbers

The Random class provides a seedable random number generator with common probability distributions. Seeding enables reproducible simulations for testing and validation.

Creating a Random Generator

typescript
import { Random } from 'discrete-sim';

// Random seed (non-reproducible)
const rng = new Random();

// Fixed seed (reproducible)
const rng = new Random(12345);

Why Use Seeded Random Numbers?

Seeding your random number generator provides several benefits:

  • Reproducibility - Run the same simulation multiple times with identical results
  • Debugging - Isolate and reproduce specific scenarios
  • Testing - Validate simulation behavior with known inputs
  • Comparison - Compare different configurations with same random sequence
typescript
// Same seed = same results
const rng1 = new Random(42);
const rng2 = new Random(42);

console.log(rng1.random());  // 0.7235
console.log(rng2.random());  // 0.7235 (same!)

Uniform Distribution

random()

Generate random numbers between 0 and 1:

typescript
const value = rng.random();  // 0 ≤ value < 1

uniform(min, max)

Generate random numbers in a range:

typescript
const price = rng.uniform(10, 50);      // 10 ≤ price < 50
const angle = rng.uniform(0, 2 * Math.PI);

randInt(min, max)

Generate random integers:

typescript
const dice = rng.randInt(1, 7);         // 1, 2, 3, 4, 5, or 6
const choice = rng.randInt(0, 10);      // 0 to 9

Exponential Distribution

Common for modeling inter-arrival times and service times:

typescript
// Mean of 5.0
const interArrivalTime = rng.exponential(5.0);

Use cases:

  • Time between customer arrivals
  • Time between equipment failures
  • Service time duration
  • Lifespan of components
typescript
function* arrivalProcess(rng: Random) {
  let id = 0;
  while (true) {
    sim.process(() => customer(id++));

    // Next arrival time (exponential)
    const nextArrival = rng.exponential(5);
    yield* timeout(nextArrival);
  }
}

Normal (Gaussian) Distribution

typescript
// Mean = 100, std dev = 15
const iqScore = rng.normal(100, 15);

Use cases:

  • Human performance variability
  • Measurement errors
  • Natural phenomena
  • Quality control (dimensions, weights)
typescript
function* manufacturing() {
  // Target: 10cm with 0.1cm variation
  const length = rng.normal(10, 0.1);

  if (length < 9.8 || length > 10.2) {
    stats.increment('defects');
  }
}

Triangular Distribution

Defined by minimum, mode, and maximum:

typescript
// min=5, mode=10, max=20
const duration = rng.triangular(5, 10, 20);

Use cases:

  • Expert estimates (best, most likely, worst case)
  • Project planning (PERT)
  • When you don't have enough data for other distributions
typescript
function* task() {
  // Optimistic: 5, Most likely: 8, Pessimistic: 15
  const duration = rng.triangular(5, 8, 15);
  yield* timeout(duration);
}

Discrete Distributions

choice(array)

Select random element from array:

typescript
const colors = ['red', 'green', 'blue'];
const color = rng.choice(colors);

const priorities = [1, 2, 3, 4, 5];
const priority = rng.choice(priorities);

sample(array, k)

Select k random elements without replacement:

typescript
const deck = ['A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K'];
const hand = rng.sample(deck, 5);  // Draw 5 cards

Examples

Variable Arrival Process

typescript
import { Simulation, Random, timeout } from 'discrete-sim';

function* customer(id: number, arrivalTime: number) {
  console.log(`Customer ${id} arrives at ${sim.now}`);

  // Service time between 3 and 7 minutes
  const serviceTime = rng.uniform(3, 7);
  yield* timeout(serviceTime);

  console.log(`Customer ${id} leaves after ${serviceTime.toFixed(1)} minutes`);
}

const sim = new Simulation();
const rng = new Random(42);  // Reproducible

function* arrivalProcess() {
  let id = 0;

  while (sim.now < 480) {  // 8 hour day
    const arrivalTime = sim.now;
    sim.process(() => customer(id++, arrivalTime));

    // Exponential inter-arrival time (mean = 5 minutes)
    const nextArrival = rng.exponential(5);
    yield* timeout(nextArrival);
  }
}

sim.process(arrivalProcess);
sim.run(480);

Manufacturing Variability

typescript
function* machine(rng: Random) {
  while (true) {
    // Normal processing time (mean=10, std=1)
    const processTime = Math.max(0.1, rng.normal(10, 1));
    yield* timeout(processTime);

    stats.increment('produced');

    // 2% chance of breakdown
    if (rng.random() < 0.02) {
      // Triangular repair time
      const repairTime = rng.triangular(10, 20, 60);
      console.log(`Breakdown! Repair time: ${repairTime.toFixed(1)}`);
      yield* timeout(repairTime);
      stats.increment('breakdowns');
    }
  }
}

const sim = new Simulation();
const stats = sim.statistics;
const rng = new Random(123);

for (let i = 0; i < 3; i++) {
  sim.process(() => machine(rng));
}

sim.run(480);

console.log(`Produced: ${stats.getCounter('produced')}`);
console.log(`Breakdowns: ${stats.getCounter('breakdowns')}`);

Task Assignment

typescript
function* dispatcher(tasks: string[], workers: Resource, rng: Random) {
  for (const task of tasks) {
    // Random task duration
    const duration = rng.triangular(5, 10, 20);

    // Random priority
    const priority = rng.randInt(1, 4);  // 1, 2, or 3

    console.log(`Task: ${task}, Priority: ${priority}, Duration: ${duration.toFixed(1)}`);

    // Assign to worker
    yield workers.request();
    yield* timeout(duration);
    workers.release();
  }
}

const sim = new Simulation();
const rng = new Random(999);
const workers = new Resource(sim, 2);

const tasks = ['Task-A', 'Task-B', 'Task-C', 'Task-D', 'Task-E'];
sim.process(() => dispatcher(tasks, workers, rng));

sim.run();

Choosing the Right Distribution

Exponential

  • When: Modeling time between independent events
  • Examples: Customer arrivals, equipment failures, phone calls

Normal

  • When: Natural variation around a mean
  • Examples: Human performance, measurement errors, quality metrics

Uniform

  • When: All values equally likely
  • Examples: Random delays, random selection, dice rolls

Triangular

  • When: Expert estimates with min/mode/max
  • Examples: Project tasks, uncertain durations, risk analysis

Best Practices

Share One Random Instance

typescript
// Good - one shared generator
const sim = new Simulation();
const rng = new Random(42);

function* process1() {
  yield* timeout(rng.exponential(5));
}

function* process2() {
  yield* timeout(rng.exponential(10));
}

Avoid Math.random()

typescript
// Bad - not reproducible
const value = Math.random();

// Good - reproducible
const value = rng.random();

Use Meaningful Seeds

typescript
// For production - random seed
const rng = new Random();

// For testing - fixed seed
const rng = new Random(42);

// For experiments - varied seeds
for (let seed = 0; seed < 30; seed++) {
  const rng = new Random(seed);
  runSimulation(rng);
}

Validate Your Distributions

typescript
// Collect samples
const samples: number[] = [];
for (let i = 0; i < 1000; i++) {
  samples.push(rng.exponential(5));
}

// Check mean
const mean = samples.reduce((a, b) => a + b) / samples.length;
console.log(`Mean: ${mean.toFixed(2)} (expected ~5.0)`);

Random API Reference

typescript
class Random {
  constructor(seed?: number)

  // Core
  random(): number                              // [0, 1)
  uniform(min: number, max: number): number     // [min, max)
  randInt(min: number, max: number): number     // [min, max)

  // Continuous distributions
  exponential(mean: number): number
  normal(mean: number, stdDev: number): number
  triangular(min: number, mode: number, max: number): number

  // Discrete
  choice<T>(array: T[]): T
  sample<T>(array: T[], k: number): T[]
}

Next Steps

Released under the MIT License.