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
| Function | Description | Return Type | Example |
|---|---|---|---|
random.random() | Random float: 0.0 ≤ x < 1.0 | float | random() → 0.8444218515 |
random.uniform(a, b) | Random float: a ≤ x ≤ b | float | uniform(1.5, 10.5) → 6.027 |
random.triangular(low, high, mode) | Triangular distribution | float | triangular(0, 10, 5) → 4.8 |
random.betavariate(alpha, beta) | Beta distribution | float | betavariate(2, 5) → 0.312 |
random.expovariate(lambd) | Exponential distribution | float | expovariate(1.5) → 0.891 |
random.gammavariate(alpha, beta) | Gamma distribution | float | gammavariate(2, 3) → 4.158 |
random.gauss(mu, sigma) | Gaussian distribution | float | gauss(0, 1) → -0.234 |
random.lognormvariate(mu, sigma) | Log normal distribution | float | lognormvariate(0, 1) → 1.649 |
random.normalvariate(mu, sigma) | Normal distribution | float | normalvariate(100, 15) → 97.4 |
random.vonmisesvariate(mu, kappa) | Von Mises distribution | float | vonmisesvariate(0, 4) → 0.123 |
random.paretovariate(alpha) | Pareto distribution | float | paretovariate(1.16) → 3.564 |
random.weibullvariate(alpha, beta) | Weibull distribution | float | weibullvariate(1, 2) → 1.337 |
Integer Random Number Generation
| Function | Description | Return Type | Example |
|---|---|---|---|
random.randrange(stop) | Random int: 0 ≤ x < stop | int | randrange(10) → 7 |
random.randrange(start, stop) | Random int: start ≤ x < stop | int | randrange(5, 15) → 12 |
random.randrange(start, stop, step) | Random int with step | int | randrange(0, 10, 2) → 6 |
random.randint(a, b) | Random int: a ≤ x ≤ b (inclusive) | int | randint(1, 10) → 7 |
random.getrandbits(k) | Random k-bit integer | int | getrandbits(8) → 142 |
Sequence Operations
| Function | Description | Return Type | Example |
|---|---|---|---|
random.choice(seq) | Random element from sequence | Any | choice([1,2,3]) → 2 |
random.choices(seq, weights, k) | k choices with replacement | list | choices([1,2,3], k=2) → [3,1] |
random.sample(population, k) | k unique elements | list | sample([1,2,3,4], 2) → [3,1] |
random.shuffle(seq) | Shuffle sequence in-place | None | shuffle([1,2,3]) → [2,3,1] |
State Management
| Function | Description | Return Type | Example |
|---|---|---|---|
random.seed(a) | Initialize random number generator | None | seed(42) |
random.getstate() | Get current state | tuple | state = getstate() |
random.setstate(state) | Restore previous state | None | setstate(state) |
Parameters and Arguments
Key Parameters for Sequence Functions
weights: Optional sequence of relative weights forchoices()cum_weights: Optional sequence of cumulative weights forchoices()k: Number of selections to makepopulation: Source sequence for sampling
Distribution Parameters
mu,sigma: Mean and standard deviation for normal distributionsalpha,beta: Shape parameters for beta/gamma distributionslambd: 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
secretsmodule 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 Category | Time Complexity | Notes |
|---|---|---|
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 functions | O(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
secretsmodule 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()orsecrets)
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)
- Library Documentation: Python random Module - Complete reference for all functions
- Tutorial: Brief Tour of Standard Library - Basic random usage
- Language Reference: Random Number Generation - Technical details
- HOWTOs: Functional Programming HOWTO - Random in functional context
- What's New: Python 3.6+ Features -
choices()function introduction - PEP 506: Adding secrets module - When NOT to use random
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
- Real Python: Working with Random Data in Python
- GeeksforGeeks: Random Module in Python
- DataCamp: Random Sampling in Python
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.