Skip to main content

random - Generate Random Numbers

The random module implements pseudo-random number generators for various distributions. This module is essential for coding interviews involving randomization algorithms, sampling, shuffling, and probabilistic computations.

Official Documentation: Python random Module
Tutorial Reference: Random Numbers

📚 Basic Usage

Simple Example

import random

# Basic random number generation
print(random.random()) # 0.8444218515250481 (0.0 <= x < 1.0)
print(random.randint(1, 10)) # 7 (1 <= x <= 10, inclusive)
print(random.uniform(1.5, 10.5)) # 6.027633129686626 (1.5 <= x <= 10.5)

# Random choice from sequence
fruits = ['apple', 'banana', 'cherry', 'date']
print(random.choice(fruits)) # 'banana'
print(random.choices(fruits, k=3)) # ['apple', 'cherry', 'apple'] (with replacement)
print(random.sample(fruits, 2)) # ['cherry', 'apple'] (without replacement)

# Shuffling
deck = list(range(1, 11))
random.shuffle(deck)
print(deck) # [3, 7, 1, 9, 4, 6, 2, 10, 5, 8] (shuffled in-place)

Core Functions

import random

# Setting seed for reproducible results
random.seed(42)
print(random.random()) # 0.6394267984578837 (always same with seed 42)

# Integer generation
print(random.randrange(10)) # 0-9 (stop excluded)
print(random.randrange(5, 15)) # 5-14 (start included, stop excluded)
print(random.randrange(0, 10, 2)) # 0, 2, 4, 6, 8 (with step)

# Floating-point generation
print(random.uniform(0, 1)) # Uniform distribution
print(random.triangular(0, 10, 5)) # Triangular distribution (low, high, mode)
print(random.gauss(0, 1)) # Gaussian/normal distribution (mu, sigma)
print(random.expovariate(1.5)) # Exponential distribution

Common Patterns

import random

# Pattern 1: Weighted random selection
choices = ['A', 'B', 'C']
weights = [0.5, 0.3, 0.2] # Probabilities sum to 1.0
selected = random.choices(choices, weights=weights, k=1)[0]
print(f"Weighted choice: {selected}")

# Pattern 2: Random sampling without replacement
population = list(range(100))
sample = random.sample(population, 10) # 10 unique numbers
print(f"Sample: {sample}")

# Pattern 3: Probability-based decisions
def maybe_do_something(probability=0.3):
"""Execute action with given probability."""
return random.random() < probability

if maybe_do_something(0.7): # 70% chance
print("Action executed!")

# Pattern 4: Random data generation for testing
def generate_test_data(n=100):
"""Generate random test data."""
return {
'integers': [random.randint(1, 1000) for _ in range(n)],
'floats': [random.uniform(0, 100) for _ in range(n)],
'booleans': [random.choice([True, False]) for _ in range(n)],
'strings': [''.join(random.choices('abcdefghijklmnopqrstuvwxyz', k=5)) for _ in range(n)]
}

🔧 Random Module API Reference

Basic Random Number Generation

FunctionDescriptionReturn TypeExample
random.random()Random float: 0.0 ≤ x < 1.0floatrandom() → 0.8444218515
random.uniform(a, b)Random float: a ≤ x ≤ bfloatuniform(1.5, 10.5) → 6.027
random.triangular(low, high, mode)Triangular distributionfloattriangular(0, 10, 5) → 4.8
random.betavariate(alpha, beta)Beta distributionfloatbetavariate(2, 5) → 0.312
random.expovariate(lambd)Exponential distributionfloatexpovariate(1.5) → 0.891
random.gammavariate(alpha, beta)Gamma distributionfloatgammavariate(2, 3) → 4.158
random.gauss(mu, sigma)Gaussian distributionfloatgauss(0, 1) → -0.234
random.lognormvariate(mu, sigma)Log normal distributionfloatlognormvariate(0, 1) → 1.649
random.normalvariate(mu, sigma)Normal distributionfloatnormalvariate(100, 15) → 97.4
random.vonmisesvariate(mu, kappa)Von Mises distributionfloatvonmisesvariate(0, 4) → 0.123
random.paretovariate(alpha)Pareto distributionfloatparetovariate(1.16) → 3.564
random.weibullvariate(alpha, beta)Weibull distributionfloatweibullvariate(1, 2) → 1.337

Integer Random Number Generation

FunctionDescriptionReturn TypeExample
random.randrange(stop)Random int: 0 ≤ x < stopintrandrange(10) → 7
random.randrange(start, stop)Random int: start ≤ x < stopintrandrange(5, 15) → 12
random.randrange(start, stop, step)Random int with stepintrandrange(0, 10, 2) → 6
random.randint(a, b)Random int: a ≤ x ≤ b (inclusive)intrandint(1, 10) → 7
random.getrandbits(k)Random k-bit integerintgetrandbits(8) → 142

Sequence Operations

FunctionDescriptionReturn TypeExample
random.choice(seq)Random element from sequenceAnychoice([1,2,3]) → 2
random.choices(seq, weights, k)k choices with replacementlistchoices([1,2,3], k=2) → [3,1]
random.sample(population, k)k unique elementslistsample([1,2,3,4], 2) → [3,1]
random.shuffle(seq)Shuffle sequence in-placeNoneshuffle([1,2,3]) → [2,3,1]

State Management

FunctionDescriptionReturn TypeExample
random.seed(a)Initialize random number generatorNoneseed(42)
random.getstate()Get current statetuplestate = getstate()
random.setstate(state)Restore previous stateNonesetstate(state)

Parameters and Arguments

Key Parameters for Sequence Functions

  • weights: Optional sequence of relative weights for choices()
  • cum_weights: Optional sequence of cumulative weights for choices()
  • k: Number of selections to make
  • population: Source sequence for sampling

Distribution Parameters

  • mu, sigma: Mean and standard deviation for normal distributions
  • alpha, beta: Shape parameters for beta/gamma distributions
  • lambd: Rate parameter for exponential distribution

Common Parameter Combinations

# Weighted selection with relative weights
random.choices(['A', 'B', 'C'], weights=[5, 3, 2], k=10)

# Weighted selection with cumulative weights
random.choices(['A', 'B', 'C'], cum_weights=[5, 8, 10], k=10)

# Normal distribution parameters
random.normalvariate(mu=100, sigma=15) # IQ scores
random.gauss(mu=0, sigma=1) # Standard normal

# Sampling parameters
random.sample(range(1000), k=50) # 50 unique numbers from 0-999
random.choices(range(10), k=100) # 100 numbers with replacement

Detailed Method Examples

Random Selection and Sampling

import random

# Example 1: Lottery number generator
def generate_lottery_numbers(total_numbers=49, picks=6):
"""Generate lottery numbers without replacement."""
return sorted(random.sample(range(1, total_numbers + 1), picks))

lottery = generate_lottery_numbers()
print(f"Lottery numbers: {lottery}") # [3, 15, 23, 31, 42, 47]

# Example 2: Weighted random selection for game items
def get_random_item():
"""Get random game item with different rarities."""
items = ['common', 'uncommon', 'rare', 'epic', 'legendary']
weights = [50, 30, 15, 4, 1] # Probability weights
return random.choices(items, weights=weights, k=1)[0]

item = get_random_item()
print(f"You got: {item}")

# Example 3: Random sampling for A/B testing
def assign_to_group(user_id, groups=['control', 'treatment'], split=0.5):
"""Assign user to A/B test group based on user ID."""
random.seed(user_id) # Consistent assignment for same user
return 'treatment' if random.random() < split else 'control'

group = assign_to_group(12345)
print(f"User assigned to: {group}")

Shuffling and Permutations

import random

# Example 1: Card deck shuffling
def create_and_shuffle_deck():
"""Create and shuffle a standard deck of cards."""
suits = ['Hearts', 'Diamonds', 'Clubs', 'Spades']
ranks = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A']
deck = [f"{rank} of {suit}" for suit in suits for rank in ranks]

random.shuffle(deck)
return deck

deck = create_and_shuffle_deck()
print(f"Top 5 cards: {deck[:5]}")

# Example 2: Fisher-Yates shuffle implementation
def fisher_yates_shuffle(arr):
"""Implement Fisher-Yates shuffle algorithm."""
arr = arr.copy() # Don't modify original
for i in range(len(arr) - 1, 0, -1):
j = random.randint(0, i)
arr[i], arr[j] = arr[j], arr[i]
return arr

original = [1, 2, 3, 4, 5]
shuffled = fisher_yates_shuffle(original)
print(f"Original: {original}, Shuffled: {shuffled}")

# Example 3: Random permutation generation
def generate_random_permutations(items, count=5):
"""Generate multiple random permutations."""
permutations = []
for _ in range(count):
perm = items.copy()
random.shuffle(perm)
permutations.append(perm)
return permutations

perms = generate_random_permutations([1, 2, 3, 4], count=3)
print(f"Random permutations: {perms}")

Statistical Distributions

import random

# Example 1: Simulating dice rolls with different distributions
def simulate_dice_rolls(sides=6, rolls=1000):
"""Simulate dice rolls and show distribution."""
results = [random.randint(1, sides) for _ in range(rolls)]

# Count occurrences
counts = {}
for result in results:
counts[result] = counts.get(result, 0) + 1

# Show distribution
for face in range(1, sides + 1):
percentage = (counts.get(face, 0) / rolls) * 100
print(f"Face {face}: {counts.get(face, 0):4d} ({percentage:5.1f}%)")

print("Dice roll simulation:")
simulate_dice_rolls(6, 6000)

# Example 2: Normal distribution for test scores
def generate_test_scores(num_students=100, mean=75, std_dev=10):
"""Generate realistic test scores using normal distribution."""
scores = []
for _ in range(num_students):
score = random.normalvariate(mean, std_dev)
# Clamp to valid range
score = max(0, min(100, score))
scores.append(round(score, 1))

return scores

scores = generate_test_scores(50, 78, 12)
print(f"Test scores: {sorted(scores)}")
print(f"Average: {sum(scores)/len(scores):.1f}")

# Example 3: Exponential distribution for arrival times
def simulate_customer_arrivals(hours=8, avg_per_hour=15):
"""Simulate customer arrival times using exponential distribution."""
arrivals = []
current_time = 0
rate = avg_per_hour / 60 # Convert to per-minute rate

while current_time < hours * 60: # Convert hours to minutes
# Time until next arrival (exponential distribution)
time_to_next = random.expovariate(rate)
current_time += time_to_next

if current_time < hours * 60:
arrivals.append(current_time)

return arrivals

arrivals = simulate_customer_arrivals(2, 20) # 2 hours, 20 customers/hour average
print(f"Customer arrivals: {[f'{t:.1f}min' for t in arrivals[:10]]}")
print(f"Total customers in 2 hours: {len(arrivals)}")

Important Notes

  • Thread Safety: The random module is not thread-safe. Use random.Random() instances for thread-safe operation
  • Cryptographic Security: This module is NOT suitable for cryptographic purposes. Use the secrets module instead
  • Reproducibility: Set seed with random.seed() for reproducible results in testing
  • Performance: random.choice() is O(1), random.sample() is O(k), random.shuffle() is O(n)

🐛 Common Errors and Troubleshooting

Typical Error Messages

import random

# Error 1: IndexError - Empty sequence
try:
empty_list = []
random.choice(empty_list) # IndexError: Cannot choose from an empty sequence
except IndexError as e:
print(f"Empty sequence error: {e}")

# Error 2: ValueError - Invalid sample size
try:
small_list = [1, 2, 3]
random.sample(small_list, 5) # ValueError: Sample larger than population
except ValueError as e:
print(f"Sample size error: {e}")

# Error 3: TypeError - Invalid weights
try:
choices = ['A', 'B', 'C']
weights = [1, 2] # Wrong length
random.choices(choices, weights=weights, k=1) # ValueError: weights sequence length
except ValueError as e:
print(f"Weights error: {e}")

# Error 4: ValueError - Negative k parameter
try:
random.sample([1, 2, 3], -1) # ValueError: Sample size must be non-negative
except ValueError as e:
print(f"Negative k error: {e}")

Debugging Tips

import random

# Reproducible random sequences for debugging
def debug_random_function(seed=42):
"""Use fixed seed for consistent debugging."""
random.seed(seed)

# Your random operations here
return [random.randint(1, 10) for _ in range(5)]

# Test with same seed multiple times
print("Debug run 1:", debug_random_function(42)) # Always same result
print("Debug run 2:", debug_random_function(42)) # Always same result

# Safe random choice with fallback
def safe_random_choice(seq, default=None):
"""Random choice with fallback for empty sequences."""
if not seq:
return default
return random.choice(seq)

print(safe_random_choice([1, 2, 3])) # Normal choice
print(safe_random_choice([], "default")) # Returns "default"

# Validate sample size
def safe_random_sample(population, k, allow_replacement=False):
"""Safe sampling with automatic fallback to choices() if needed."""
if k > len(population):
if allow_replacement:
return random.choices(population, k=k)
else:
raise ValueError(f"Cannot sample {k} items from population of {len(population)}")
return random.sample(population, k)

# Weight validation
def safe_weighted_choice(choices, weights=None, k=1):
"""Safe weighted choice with validation."""
if weights is not None:
if len(weights) != len(choices):
raise ValueError("Weights must have same length as choices")
if any(w < 0 for w in weights):
raise ValueError("Weights must be non-negative")
if sum(weights) == 0:
raise ValueError("At least one weight must be positive")

return random.choices(choices, weights=weights, k=k)

# Example usage
try:
result = safe_weighted_choice(['A', 'B', 'C'], [1, 2, 3], k=2)
print(f"Weighted choice: {result}")
except ValueError as e:
print(f"Weight validation error: {e}")

Error Handling Patterns

import random

def robust_random_operations(data, sample_size=5, weights=None):
"""Demonstrate robust error handling for random operations."""
try:
# Validate inputs
if not data:
raise ValueError("Data sequence cannot be empty")

if not isinstance(sample_size, int) or sample_size < 0:
raise ValueError("Sample size must be non-negative integer")

# Handle different scenarios
if sample_size == 0:
return []

if sample_size == 1:
# Use choice for single item
if weights:
return random.choices(data, weights=weights, k=1)
else:
return [random.choice(data)]

# Multiple items
if sample_size > len(data):
# Use choices with replacement
if weights:
return random.choices(data, weights=weights, k=sample_size)
else:
return random.choices(data, k=sample_size)
else:
# Use sample without replacement
if weights:
# Can't use weights with sample, fall back to choices
return random.choices(data, weights=weights, k=sample_size)
else:
return random.sample(data, sample_size)

except (ValueError, TypeError, IndexError) as e:
print(f"Random operation failed: {e}")
return None

# Example usage
data = ['apple', 'banana', 'cherry', 'date']
weights = [0.4, 0.3, 0.2, 0.1]

print(robust_random_operations(data, 2)) # Normal sampling
print(robust_random_operations(data, 6, weights)) # With replacement
print(robust_random_operations([], 5)) # Empty data handling
print(robust_random_operations(data, -1)) # Invalid size handling

🎯 Primary Use Cases

1. Algorithm Testing and Simulation

Use Case: Generate random test data and simulate algorithm behavior
Why random: Provides comprehensive testing with varied inputs and statistical analysis
Code Example:

import random
import time

def generate_test_cases(algorithm_func, num_tests=1000):
"""Generate random test cases for algorithm validation."""
test_results = {
'passed': 0,
'failed': 0,
'execution_times': [],
'edge_cases': []
}

for i in range(num_tests):
# Generate random input data
size = random.randint(1, 100)
data = [random.randint(-1000, 1000) for _ in range(size)]

# Add some edge cases
if i % 100 == 0: # 1% edge cases
edge_case_type = random.choice(['empty', 'single', 'duplicates', 'sorted'])
if edge_case_type == 'empty':
data = []
elif edge_case_type == 'single':
data = [random.randint(-1000, 1000)]
elif edge_case_type == 'duplicates':
value = random.randint(1, 10)
data = [value] * random.randint(5, 20)
elif edge_case_type == 'sorted':
data = sorted(data)

test_results['edge_cases'].append((edge_case_type, len(data)))

# Time the algorithm execution
start_time = time.time()
try:
result = algorithm_func(data)
execution_time = time.time() - start_time
test_results['execution_times'].append(execution_time)
test_results['passed'] += 1

# Validate result (example for sorting algorithm)
if data and result != sorted(data):
test_results['failed'] += 1
test_results['passed'] -= 1

except Exception as e:
test_results['failed'] += 1
print(f"Algorithm failed on test {i}: {e}")

# Calculate statistics
if test_results['execution_times']:
avg_time = sum(test_results['execution_times']) / len(test_results['execution_times'])
max_time = max(test_results['execution_times'])

print(f"Test Results:")
print(f" Passed: {test_results['passed']}/{num_tests}")
print(f" Failed: {test_results['failed']}/{num_tests}")
print(f" Average execution time: {avg_time:.6f}s")
print(f" Maximum execution time: {max_time:.6f}s")
print(f" Edge cases tested: {len(test_results['edge_cases'])}")

return test_results

# Example: Test a sorting algorithm
def bubble_sort(arr):
"""Simple bubble sort for testing."""
if not arr:
return []

arr = arr.copy()
n = len(arr)
for i in range(n):
for j in range(0, n - i - 1):
if arr[j] > arr[j + 1]:
arr[j], arr[j + 1] = arr[j + 1], arr[j]
return arr

# Run comprehensive tests
random.seed(42) # Reproducible testing
results = generate_test_cases(bubble_sort, 500)

2. Randomized Algorithms Implementation

Use Case: Implement algorithms that use randomization for efficiency or correctness
Why random: Essential for quicksort, randomized selection, Monte Carlo methods
Code Example:

import random

def randomized_quicksort(arr):
"""Quicksort with random pivot selection for better average case."""
if len(arr) <= 1:
return arr

# Random pivot selection
pivot_index = random.randint(0, len(arr) - 1)
pivot = arr[pivot_index]

# Partition around pivot
left = [x for x in arr if x < pivot]
middle = [x for x in arr if x == pivot]
right = [x for x in arr if x > pivot]

return randomized_quicksort(left) + middle + randomized_quicksort(right)

def randomized_select(arr, k):
"""Find k-th smallest element using randomized selection."""
if len(arr) == 1:
return arr[0]

# Random pivot
pivot = random.choice(arr)

# Partition
left = [x for x in arr if x < pivot]
middle = [x for x in arr if x == pivot]
right = [x for x in arr if x > pivot]

if k < len(left):
return randomized_select(left, k)
elif k < len(left) + len(middle):
return pivot
else:
return randomized_select(right, k - len(left) - len(middle))

def monte_carlo_pi(num_samples=1000000):
"""Estimate π using Monte Carlo method."""
inside_circle = 0

for _ in range(num_samples):
x = random.uniform(-1, 1)
y = random.uniform(-1, 1)

if x*x + y*y <= 1:
inside_circle += 1

pi_estimate = 4 * inside_circle / num_samples
return pi_estimate

def randomized_graph_coloring(graph, max_colors=4):
"""Simple randomized graph coloring algorithm."""
coloring = {}
colors = list(range(max_colors))

# Randomize vertex order for better results
vertices = list(graph.keys())
random.shuffle(vertices)

for vertex in vertices:
# Get colors used by neighbors
used_colors = set()
for neighbor in graph.get(vertex, []):
if neighbor in coloring:
used_colors.add(coloring[neighbor])

# Choose random available color
available_colors = [c for c in colors if c not in used_colors]
if available_colors:
coloring[vertex] = random.choice(available_colors)
else:
return None # No valid coloring found

return coloring

# Example usage
random.seed(42)

# Test randomized quicksort
test_data = [random.randint(1, 100) for _ in range(20)]
print(f"Original: {test_data}")
sorted_data = randomized_quicksort(test_data)
print(f"Sorted: {sorted_data}")

# Test randomized selection
k = 5 # Find 5th smallest
kth_element = randomized_select(test_data, k)
print(f"{k}th smallest element: {kth_element}")

# Estimate π
pi_est = monte_carlo_pi(100000)
print(f"π estimate: {pi_est:.6f} (actual: 3.141593)")

# Graph coloring example
graph = {
'A': ['B', 'C'],
'B': ['A', 'C', 'D'],
'C': ['A', 'B', 'D'],
'D': ['B', 'C']
}
coloring = randomized_graph_coloring(graph)
print(f"Graph coloring: {coloring}")

3. Sampling and Data Analysis

Use Case: Sample data for analysis, A/B testing, and statistical validation
Why random: Provides unbiased sampling and proper statistical distribution
Code Example:

import random
from collections import Counter

def stratified_sampling(population, strata_key, sample_size):
"""Perform stratified sampling to maintain population proportions."""
# Group population by strata
strata = {}
for item in population:
key = strata_key(item)
if key not in strata:
strata[key] = []
strata[key].append(item)

# Calculate sample size for each stratum
total_population = len(population)
sample = []

for stratum_name, stratum_data in strata.items():
stratum_proportion = len(stratum_data) / total_population
stratum_sample_size = int(sample_size * stratum_proportion)

if stratum_sample_size > 0:
stratum_sample = random.sample(stratum_data,
min(stratum_sample_size, len(stratum_data)))
sample.extend(stratum_sample)

return sample

def bootstrap_sampling(data, num_samples=1000, sample_size=None):
"""Perform bootstrap sampling for statistical analysis."""
if sample_size is None:
sample_size = len(data)

bootstrap_samples = []
for _ in range(num_samples):
bootstrap_sample = random.choices(data, k=sample_size)
bootstrap_samples.append(bootstrap_sample)

return bootstrap_samples

def ab_test_assignment(user_ids, test_groups=['control', 'treatment'], ratios=None):
"""Assign users to A/B test groups with consistent assignment."""
if ratios is None:
ratios = [1.0 / len(test_groups)] * len(test_groups)

# Create cumulative distribution
cumulative_ratios = []
cumsum = 0
for ratio in ratios:
cumsum += ratio
cumulative_ratios.append(cumsum)

assignments = {}
for user_id in user_ids:
# Use user_id as seed for consistent assignment
random.seed(user_id)
rand_val = random.random()

# Find which group this user belongs to
for i, cum_ratio in enumerate(cumulative_ratios):
if rand_val <= cum_ratio:
assignments[user_id] = test_groups[i]
break

return assignments

def reservoir_sampling(stream, k):
"""Sample k items from a stream of unknown length."""
reservoir = []

for i, item in enumerate(stream):
if i < k:
reservoir.append(item)
else:
# Randomly replace elements with decreasing probability
j = random.randint(0, i)
if j < k:
reservoir[j] = item

return reservoir

# Example usage
random.seed(42)

# Create sample population with different strata
population = []
for age_group in ['18-25', '26-35', '36-45', '46-55', '55+']:
for gender in ['M', 'F']:
count = random.randint(50, 200) # Different stratum sizes
for i in range(count):
population.append({
'id': len(population),
'age_group': age_group,
'gender': gender,
'score': random.normalvariate(75, 15)
})

print(f"Total population: {len(population)}")

# Stratified sampling by age group
age_sample = stratified_sampling(
population,
lambda x: x['age_group'],
100
)

age_distribution = Counter(person['age_group'] for person in age_sample)
print(f"Age distribution in sample: {dict(age_distribution)}")

# Bootstrap analysis of mean scores
scores = [person['score'] for person in population[:1000]] # Subset for demo
bootstrap_means = []

for bootstrap_sample in bootstrap_sampling(scores, 1000, 100):
bootstrap_means.append(sum(bootstrap_sample) / len(bootstrap_sample))

print(f"Bootstrap mean estimates:")
print(f" Original mean: {sum(scores) / len(scores):.2f}")
print(f" Bootstrap mean: {sum(bootstrap_means) / len(bootstrap_means):.2f}")
print(f" 95% CI: [{sorted(bootstrap_means)[25]:.2f}, {sorted(bootstrap_means)[975]:.2f}]")

# A/B test assignment
user_ids = list(range(1000, 2000))
ab_assignments = ab_test_assignment(
user_ids,
['control', 'treatment_a', 'treatment_b'],
[0.5, 0.25, 0.25]
)

assignment_counts = Counter(ab_assignments.values())
print(f"A/B test assignments: {dict(assignment_counts)}")

# Reservoir sampling from a large stream
def generate_stream():
"""Simulate a large data stream."""
for i in range(10000):
yield f"item_{i}"

stream_sample = reservoir_sampling(generate_stream(), 50)
print(f"Reservoir sample size: {len(stream_sample)}")
print(f"Sample items: {stream_sample[:5]}...{stream_sample[-5:]}")

4. Game Development and Procedural Generation

Use Case: Generate random content, handle probability in games, create procedural worlds
Why random: Essential for creating varied, engaging, and unpredictable game experiences
Code Example:

import random

class LootGenerator:
"""Generate random loot with rarity-based probabilities."""

def __init__(self):
self.rarities = {
'common': {'weight': 60, 'color': 'gray'},
'uncommon': {'weight': 25, 'color': 'green'},
'rare': {'weight': 10, 'color': 'blue'},
'epic': {'weight': 4, 'color': 'purple'},
'legendary': {'weight': 1, 'color': 'orange'}
}

self.item_types = ['weapon', 'armor', 'accessory', 'consumable']

self.prefixes = ['Mighty', 'Swift', 'Ancient', 'Cursed', 'Blessed', 'Flaming']
self.suffixes = ['of Power', 'of Speed', 'of Wisdom', 'of Destruction', 'of Protection']

def generate_item(self, player_level=1, luck_modifier=1.0):
"""Generate a random item based on player level and luck."""
# Adjust rarity weights based on luck
adjusted_weights = []
rarities_list = list(self.rarities.keys())

for rarity in rarities_list:
weight = self.rarities[rarity]['weight']
# Higher luck increases chance of rare items
if rarity in ['epic', 'legendary']:
weight *= luck_modifier
adjusted_weights.append(weight)

# Select rarity
rarity = random.choices(rarities_list, weights=adjusted_weights, k=1)[0]

# Generate item properties
item_type = random.choice(self.item_types)
base_value = random.randint(10, 100) * player_level

# Add name modifiers for rare+ items
name_parts = [item_type.title()]
if rarity in ['rare', 'epic', 'legendary']:
if random.random() < 0.7: # 70% chance for prefix
name_parts.insert(0, random.choice(self.prefixes))
if random.random() < 0.5: # 50% chance for suffix
name_parts.append(random.choice(self.suffixes))

# Calculate final stats
rarity_multiplier = {'common': 1, 'uncommon': 1.2, 'rare': 1.5, 'epic': 2, 'legendary': 3}[rarity]
final_value = int(base_value * rarity_multiplier)

return {
'name': ' '.join(name_parts),
'type': item_type,
'rarity': rarity,
'value': final_value,
'color': self.rarities[rarity]['color'],
'stats': self.generate_stats(rarity, item_type)
}

def generate_stats(self, rarity, item_type):
"""Generate random stats based on rarity and type."""
base_stats = {'attack': 0, 'defense': 0, 'speed': 0, 'magic': 0}

# Determine primary stats by item type
primary_stats = {
'weapon': ['attack', 'magic'],
'armor': ['defense'],
'accessory': ['speed', 'magic'],
'consumable': ['attack', 'defense', 'speed', 'magic']
}

rarity_stat_count = {'common': 1, 'uncommon': 2, 'rare': 3, 'epic': 4, 'legendary': 5}[rarity]
rarity_multiplier = {'common': 1, 'uncommon': 1.5, 'rare': 2, 'epic': 3, 'legendary': 5}[rarity]

# Add primary stats
relevant_stats = primary_stats[item_type]
for stat in random.sample(relevant_stats, min(len(relevant_stats), rarity_stat_count)):
base_stats[stat] = random.randint(1, 10) * rarity_multiplier

return base_stats

class DungeonGenerator:
"""Generate random dungeon layouts."""

def __init__(self, width=20, height=20):
self.width = width
self.height = height
self.rooms = []
self.corridors = []

def generate_dungeon(self, num_rooms=8):
"""Generate a random dungeon with rooms and corridors."""
self.rooms = []
self.corridors = []

# Generate random rooms
for _ in range(num_rooms):
attempts = 0
while attempts < 50: # Avoid infinite loops
room = self.generate_room()
if not self.room_overlaps(room):
self.rooms.append(room)
break
attempts += 1

# Connect rooms with corridors
for i in range(len(self.rooms) - 1):
corridor = self.create_corridor(self.rooms[i], self.rooms[i + 1])
self.corridors.extend(corridor)

return self.create_dungeon_map()

def generate_room(self):
"""Generate a random room."""
min_size, max_size = 4, 8
width = random.randint(min_size, max_size)
height = random.randint(min_size, max_size)

x = random.randint(1, self.width - width - 1)
y = random.randint(1, self.height - height - 1)

return {'x': x, 'y': y, 'width': width, 'height': height}

def room_overlaps(self, new_room):
"""Check if room overlaps with existing rooms."""
for room in self.rooms:
if (new_room['x'] < room['x'] + room['width'] + 1 and
new_room['x'] + new_room['width'] + 1 > room['x'] and
new_room['y'] < room['y'] + room['height'] + 1 and
new_room['y'] + new_room['height'] + 1 > room['y']):
return True
return False

def create_corridor(self, room1, room2):
"""Create corridor between two rooms."""
# Get center points
x1 = room1['x'] + room1['width'] // 2
y1 = room1['y'] + room1['height'] // 2
x2 = room2['x'] + room2['width'] // 2
y2 = room2['y'] + room2['height'] // 2

corridor = []

# Create L-shaped corridor
if random.choice([True, False]):
# Horizontal first, then vertical
for x in range(min(x1, x2), max(x1, x2) + 1):
corridor.append((x, y1))
for y in range(min(y1, y2), max(y1, y2) + 1):
corridor.append((x2, y))
else:
# Vertical first, then horizontal
for y in range(min(y1, y2), max(y1, y2) + 1):
corridor.append((x1, y))
for x in range(min(x1, x2), max(x1, x2) + 1):
corridor.append((x, y2))

return corridor

def create_dungeon_map(self):
"""Create 2D map representation."""
dungeon_map = [['#' for _ in range(self.width)] for _ in range(self.height)]

# Add rooms
for room in self.rooms:
for y in range(room['y'], room['y'] + room['height']):
for x in range(room['x'], room['x'] + room['width']):
dungeon_map[y][x] = '.'

# Add corridors
for x, y in self.corridors:
if 0 <= x < self.width and 0 <= y < self.height:
dungeon_map[y][x] = '.'

return dungeon_map

# Example usage
random.seed(42)

# Generate random loot
loot_gen = LootGenerator()
print("Generated Items:")
for i in range(5):
item = loot_gen.generate_item(player_level=10, luck_modifier=1.5)
print(f" {item['name']} ({item['rarity']}) - Value: {item['value']}")
print(f" Stats: {item['stats']}")

# Generate random dungeon
dungeon_gen = DungeonGenerator(15, 15)
dungeon_map = dungeon_gen.generate_dungeon(6)

print("\nGenerated Dungeon:")
for row in dungeon_map:
print(''.join(row))

# Simulate random encounters
def random_encounter():
"""Generate random encounter based on probabilities."""
encounters = {
'treasure': 0.15,
'monster': 0.60,
'trap': 0.10,
'merchant': 0.05,
'nothing': 0.10
}

return random.choices(
list(encounters.keys()),
weights=list(encounters.values()),
k=1
)[0]

print("\nRandom Encounters:")
encounter_results = [random_encounter() for _ in range(20)]
encounter_counts = Counter(encounter_results)
for encounter, count in encounter_counts.items():
print(f" {encounter}: {count}")

Performance Considerations

Time Complexity Summary

Operation CategoryTime ComplexityNotes
random(), uniform(), gauss()O(1)Single random number generation
randint(a, b)O(1)Integer in range
choice(seq)O(1)Single random choice
choices(seq, k=n)O(n)Multiple choices with replacement
sample(seq, k)O(k)Without replacement, k ≤ len(seq)
shuffle(seq)O(n)Fisher-Yates shuffle
Distribution functionsO(1)Most statistical distributions

Basic Benchmarking

import random
import timeit

def benchmark_random_operations():
"""Compare performance of different random operations."""
setup_code = "import random; data = list(range(1000))"

# Test 1: Basic number generation
random_time = timeit.timeit("random.random()", setup=setup_code, number=100000)
randint_time = timeit.timeit("random.randint(1, 1000)", setup=setup_code, number=100000)
uniform_time = timeit.timeit("random.uniform(0, 1000)", setup=setup_code, number=100000)

print("Number Generation (100k operations):")
print(f" random(): {random_time:.4f}s")
print(f" randint(): {randint_time:.4f}s")
print(f" uniform(): {uniform_time:.4f}s")

# Test 2: Sequence operations
choice_time = timeit.timeit("random.choice(data)", setup=setup_code, number=10000)
choices_time = timeit.timeit("random.choices(data, k=10)", setup=setup_code, number=10000)
sample_time = timeit.timeit("random.sample(data, 10)", setup=setup_code, number=10000)

print("\nSequence Operations (10k operations):")
print(f" choice(): {choice_time:.4f}s")
print(f" choices(k=10): {choices_time:.4f}s")
print(f" sample(k=10): {sample_time:.4f}s")

# Test 3: Shuffle performance
shuffle_setup = "import random"
shuffle_100 = timeit.timeit("data=list(range(100)); random.shuffle(data)",
setup=shuffle_setup, number=10000)
shuffle_1000 = timeit.timeit("data=list(range(1000)); random.shuffle(data)",
setup=shuffle_setup, number=1000)

print("\nShuffle Performance:")
print(f" 100 items (10k ops): {shuffle_100:.4f}s")
print(f" 1000 items (1k ops): {shuffle_1000:.4f}s")

# Performance comparison for different sampling methods
def compare_sampling_methods():
"""Compare different sampling approaches."""
data = list(range(10000))

def method1_multiple_choice():
return [random.choice(data) for _ in range(100)]

def method2_choices():
return random.choices(data, k=100)

def method3_sample():
return random.sample(data, 100)

time1 = timeit.timeit(method1_multiple_choice, number=1000)
time2 = timeit.timeit(method2_choices, number=1000)
time3 = timeit.timeit(method3_sample, number=1000)

print("\nSampling Method Comparison (1k operations, 100 samples each):")
print(f" Multiple choice(): {time1:.4f}s")
print(f" choices(k=100): {time2:.4f}s")
print(f" sample(k=100): {time3:.4f}s")

benchmark_random_operations()
compare_sampling_methods()

Memory Usage Tips

import random
import sys

# Memory-efficient patterns
def memory_efficient_random():
"""Demonstrate memory-efficient random operations."""

# Use generators for large random sequences
def random_generator(count):
"""Generate random numbers without storing all in memory."""
for _ in range(count):
yield random.random()

# Process large random datasets in chunks
def process_large_dataset(total_size, chunk_size=1000):
"""Process large random dataset in memory-efficient chunks."""
for start in range(0, total_size, chunk_size):
end = min(start + chunk_size, total_size)
chunk = [random.random() for _ in range(end - start)]

# Process chunk (example: calculate statistics)
chunk_sum = sum(chunk)
chunk_avg = chunk_sum / len(chunk)

yield chunk_avg

# Efficient random sampling from large populations
def efficient_population_sampling(population_size, sample_size):
"""Sample from large population without loading it all."""
if sample_size > population_size:
raise ValueError("Sample size cannot exceed population size")

# Use set to avoid duplicates
sampled_indices = set()
while len(sampled_indices) < sample_size:
sampled_indices.add(random.randrange(population_size))

return sorted(sampled_indices)

# Memory usage comparison
print("Memory Usage Examples:")

# Large list vs generator
large_list = [random.random() for _ in range(100000)]
generator = random_generator(100000)

print(f"Large list memory: {sys.getsizeof(large_list)} bytes")
print(f"Generator memory: {sys.getsizeof(generator)} bytes")

# Efficient sampling
indices = efficient_population_sampling(1000000, 1000)
print(f"Sampled {len(indices)} indices from 1M population")

# Process large dataset in chunks
chunk_averages = list(process_large_dataset(50000, 1000))
print(f"Processed 50k random numbers in {len(chunk_averages)} chunks")

memory_efficient_random()

# Thread-safe random operations
def thread_safe_random():
"""Demonstrate thread-safe random number generation."""
# Create separate Random instances for thread safety
thread_random = random.Random()
thread_random.seed(42)

# Each thread should use its own Random instance
def worker_function(worker_id):
worker_random = random.Random(worker_id) # Seed with worker ID
return [worker_random.random() for _ in range(100)]

# Example of thread-safe usage
worker_results = []
for worker_id in range(4):
results = worker_function(worker_id)
worker_results.append(results[:5]) # First 5 for display

print("\nThread-safe Random Results:")
for i, results in enumerate(worker_results):
print(f"Worker {i}: {[f'{x:.3f}' for x in results]}")

thread_safe_random()

🎯 When to Use random

✅ Ideal Use Cases

  • Algorithm testing and validation with varied random inputs
  • Randomized algorithms (quicksort, Monte Carlo, genetic algorithms)
  • Game development for procedural generation and probability mechanics
  • Sampling and statistical analysis for data science and A/B testing
  • Simulation and modeling for scientific and business applications
  • Shuffling and permutation operations for fair ordering
  • Mock data generation for testing and development
  • Load balancing with random distribution strategies

❌ When NOT to Use random

  • Cryptographic applications (use secrets module instead)
  • Security-sensitive operations requiring true randomness
  • Reproducible scientific computing without explicit seeding
  • Performance-critical tight loops (consider pre-generating random values)
  • Distributed systems requiring synchronized randomness across nodes
  • Financial calculations requiring audit trails of random decisions
  • Situations requiring true entropy (use os.urandom() or secrets)

Alternative Solutions

import random
import secrets
import os
import numpy as np # External library example

# Alternative 1: Cryptographically secure random
secure_token = secrets.token_hex(16)
secure_int = secrets.randbelow(100)
print(f"Secure token: {secure_token}")
print(f"Secure int: {secure_int}")

# Alternative 2: OS-level randomness
os_random = os.urandom(16)
print(f"OS random bytes: {os_random.hex()}")

# Alternative 3: NumPy for numerical computing (if available)
try:
import numpy as np
np_random = np.random.rand(5)
print(f"NumPy random: {np_random}")
except ImportError:
print("NumPy not available")

# Alternative 4: Deterministic approaches where appropriate
def deterministic_shuffle(items, seed_value):
"""Deterministic shuffle based on seed."""
items = items.copy()
random.seed(seed_value)
random.shuffle(items)
return items

# Alternative 5: Custom random number generators
class LinearCongruentialGenerator:
"""Simple custom RNG for educational purposes."""

def __init__(self, seed=1):
self.current = seed
self.a = 1664525
self.c = 1013904223
self.m = 2**32

def next(self):
self.current = (self.a * self.current + self.c) % self.m
return self.current / self.m

custom_rng = LinearCongruentialGenerator(42)
custom_numbers = [custom_rng.next() for _ in range(5)]
print(f"Custom RNG: {[f'{x:.3f}' for x in custom_numbers]}")

Migration Strategies

# Migrating from basic random to more sophisticated approaches
def migration_examples():
"""Examples of when and how to migrate random usage."""

# Before: Simple random testing
def old_test_function(func):
test_data = [random.randint(1, 100) for _ in range(100)]
return all(func(x) > 0 for x in test_data)

# After: Property-based testing approach
def new_test_function(func, num_tests=1000):
random.seed(42) # Reproducible tests
for _ in range(num_tests):
# Generate more diverse test cases
test_value = random.choice([
random.randint(-1000, 1000), # Regular integers
random.uniform(-100, 100), # Floats
0, # Edge case: zero
random.randint(1, 10) ** 10 # Large numbers
])

try:
result = func(test_value)
assert isinstance(result, (int, float)), f"Invalid return type for {test_value}"
except Exception as e:
print(f"Function failed on input {test_value}: {e}")
return False
return True

# Before: Simple random data generation
def old_generate_users(count):
users = []
for i in range(count):
users.append({
'id': i,
'age': random.randint(18, 80),
'name': f"User{i}"
})
return users

# After: More realistic random data generation
def new_generate_users(count):
first_names = ['Alice', 'Bob', 'Charlie', 'Diana', 'Eve', 'Frank']
last_names = ['Smith', 'Johnson', 'Williams', 'Brown', 'Jones']

users = []
for i in range(count):
age = int(random.normalvariate(35, 12)) # More realistic age distribution
age = max(18, min(80, age)) # Clamp to valid range

users.append({
'id': i,
'age': age,
'name': f"{random.choice(first_names)} {random.choice(last_names)}",
'registration_date': random.randint(1, 365), # Days ago
'active': random.choices([True, False], weights=[0.8, 0.2], k=1)[0]
})
return users

return old_test_function, new_test_function, old_generate_users, new_generate_users

# Performance migration: from multiple calls to batch operations
def performance_migration():
"""Show migration from inefficient to efficient random operations."""

# Before: Multiple individual calls
def old_generate_dataset(size):
data = []
for _ in range(size):
data.append(random.random())
return data

# After: Batch operations where possible
def new_generate_dataset(size):
# For simple cases, list comprehension is still good
# For complex cases, consider generators or chunked processing
if size < 10000:
return [random.random() for _ in range(size)]
else:
# Process in chunks for very large datasets
chunk_size = 1000
data = []
for start in range(0, size, chunk_size):
chunk_end = min(start + chunk_size, size)
chunk = [random.random() for _ in range(chunk_end - start)]
data.extend(chunk)
return data

# Before: Inefficient sampling
def old_weighted_sample(items, weights, count):
results = []
for _ in range(count):
results.append(random.choices(items, weights=weights, k=1)[0])
return results

# After: Efficient batch sampling
def new_weighted_sample(items, weights, count):
return random.choices(items, weights=weights, k=count)

return old_generate_dataset, new_generate_dataset, old_weighted_sample, new_weighted_sample

Additional Learning Resources

Official Python Resources (PRIMARY SOURCES)

Books and Publications

  • "Introduction to Algorithms" by Cormen et al. - Randomized algorithms theory
  • "The Art of Computer Programming, Vol. 2" by Knuth - Random number generation theory
  • "Python Tricks" by Dan Bader - Practical random module usage patterns
  • "Effective Python" by Brett Slatkin - Best practices for random operations

Online Tutorials and Courses

Practice and Examples

  • LeetCode: Problems involving shuffling, sampling, and randomized algorithms
  • HackerRank: Probability and statistics domain challenges
  • Codewars: Random number generation and probability katas
  • Project Euler: Mathematical problems often requiring Monte Carlo methods

Advanced Topics

  • Randomized Algorithms: QuickSort, Skip Lists, Bloom Filters
  • Monte Carlo Methods: Numerical integration, optimization
  • Statistical Sampling: Bootstrap, cross-validation, A/B testing
  • Game Theory: Random strategies and equilibrium analysis
  • Procedural Generation: Perlin noise, L-systems, cellular automata

Community Resources

  • r/Python: Discussions on random algorithms and applications
  • r/MachineLearning: Random sampling for ML applications
  • Stack Overflow: python-random tag

This documentation follows the Python Standard Library Reference style and includes examples adapted from the official Python documentation. For the most up-to-date information, always refer to the official Python documentation.