Python隨機數產生完整教學

Python Random Number Generator Complete Tutorial【2025】| From Basics to Advanced with 15+ Code Examples

Ever needed to simulate dice rolls, shuffle a playlist, or generate test data in Python?

Random number generation is one of Python's most versatile features. Whether you're building games, conducting data science experiments, or generating secure passwords, Python's random module has you covered.

But there's a catch: using random.randint() incorrectly can introduce subtle bugs. Choosing the wrong function can make your code 10x slower. And using random for security? That's a critical vulnerability.

In this comprehensive tutorial, you'll master Python random number generation from fundamentals to advanced techniques. With 15+ practical code examples, you'll learn exactly when to use each function, how to avoid common pitfalls, and how to optimize for performance.

🚀 Quick Start: Want to generate random numbers visually first? Try our Random Number Generator tool to understand the concepts, then implement in Python.

Python random 模組常用函數速查表,展示 15 個主要方法與範例
Python random 模組常用函數速查表,展示 15 個主要方法與範例

Introduction: Why Master Python Random Number Generation?

Random number generation in Python powers countless applications:

Game Development: Procedural level generation, loot drops, enemy AI behavior
Data Science: Random sampling, Monte Carlo simulations, statistical bootstrapping
Testing: Generating test data, fuzzing for security vulnerabilities
Web Development: Session IDs, temporary tokens, captcha verification
Machine Learning: Data shuffling, dropout layers, random initialization

Python's Random Number Arsenal:

  1. random module: General-purpose PRNG (pseudo-random number generator)
  2. secrets module: Cryptographically secure random numbers
  3. numpy.random: High-performance random number generation for arrays
  4. random.SystemRandom: OS-level randomness source

This tutorial focuses primarily on the random module - the most commonly used tool - while covering when to switch to alternatives.

What You'll Learn:
- Core random functions and when to use each
- Generating unique random numbers without duplicates
- Understanding seeds for reproducible randomness
- Security-safe random number generation with secrets
- Performance optimization techniques
- Common mistakes and how to avoid them
- Real-world applications with complete code examples

Prerequisites: Basic Python knowledge (variables, functions, lists). No advanced concepts required.

For a broader understanding of random number generation concepts, check our Random Number Generator Complete Guide.

隨機數分布視覺化圖表,比較均勻分布、常態分布、三角分布等不同分布類型
隨機數分布視覺化圖表,比較均勻分布、常態分布、三角分布等不同分布類型

Python Random Module Fundamentals

Installing and Importing

The random module is part of Python's standard library - no installation needed.

import random

# Check Python version (random module available since Python 2.3)
import sys
print(f"Python version: {sys.version}")

That's it! You're ready to generate random numbers.

Core Functions Overview

Python's random module provides multiple functions for different use cases:

Function Purpose Example Output
random.randint(a, b) Random integer between a and b (inclusive) 42
random.random() Random float between 0.0 and 1.0 0.73491
random.uniform(a, b) Random float between a and b 5.284
random.choice(seq) Random element from sequence 'apple'
random.choices(seq, k=n) n random elements (with replacement) ['red', 'blue', 'red']
random.sample(seq, k=n) n unique random elements [3, 7, 1, 9, 5]
random.shuffle(seq) Shuffle sequence in-place [3, 1, 4, 2, 5]
random.seed(a) Initialize random number generator -

Let's explore each in detail with practical examples.

進階應用場景代碼範例集,包含遊戲開發、數據科學、模擬測試等實際應用
進階應用場景代碼範例集,包含遊戲開發、數據科學、模擬測試等實際應用

Basic Random Number Generation

1. random.randint() - Random Integers

Use Case: Generate random integers within a specific range.

Syntax: random.randint(a, b) - Returns random integer N such that a <= N <= b (both inclusive)

Basic Example:

import random

# Generate random number between 1 and 100 (inclusive)
number = random.randint(1, 100)
print(f"Random number: {number}")  # e.g., 42

# Dice roll (1-6)
dice = random.randint(1, 6)
print(f"You rolled: {dice}")  # e.g., 4

# Coin flip (0 or 1)
coin = random.randint(0, 1)
result = "Heads" if coin == 1 else "Tails"
print(f"Coin flip: {result}")

Important Note: Both boundaries are inclusive. random.randint(1, 10) can return 1, 2, 3... 9, or 10.

Common Mistake:

# ❌ Wrong - assumes upper bound is exclusive
number = random.randint(1, 101)  # Trying to get 1-100

# ✅ Correct
number = random.randint(1, 100)  # Returns 1-100

Practical Application - Generate Lottery Numbers:

def generate_lottery_numbers(count=6, min_num=1, max_num=49):
    """Generate lottery numbers (may contain duplicates)"""
    numbers = [random.randint(min_num, max_num) for _ in range(count)]
    return sorted(numbers)

lottery = generate_lottery_numbers()
print(f"Lottery numbers: {lottery}")
# Output: [7, 12, 23, 23, 34, 41] - note duplicate 23

Performance: randint() is fast, generating ~10 million numbers per second on modern hardware.

2. random.random() - Random Floats (0.0 to 1.0)

Use Case: Generate random decimal numbers between 0 and 1, often used as probabilities or for scaling to custom ranges.

Syntax: random.random() - Returns float 0.0 <= x < 1.0 (includes 0.0, excludes 1.0)

Basic Example:

import random

# Random float between 0 and 1
value = random.random()
print(f"Random float: {value}")  # e.g., 0.7349171293

# Simulate probability (30% chance)
if random.random() < 0.3:
    print("Lucky! 30% event occurred")
else:
    print("No luck this time")

Scaling to Custom Range:

# Scale to 0-100
percentage = random.random() * 100
print(f"Percentage: {percentage:.2f}%")  # e.g., 73.49%

# Scale to any range [a, b]
def random_float_range(a, b):
    return a + (b - a) * random.random()

# Random float between 5.0 and 10.0
value = random_float_range(5.0, 10.0)
print(f"Random float 5-10: {value:.2f}")  # e.g., 7.35

Practical Application - Game Loot Drop System:

def determine_loot_rarity():
    """Determine loot rarity based on drop rates"""
    roll = random.random()

    if roll < 0.50:  # 50% chance
        return "Common"
    elif roll < 0.75:  # 25% chance
        return "Uncommon"
    elif roll < 0.90:  # 15% chance
        return "Rare"
    elif roll < 0.97:  # 7% chance
        return "Epic"
    else:  # 3% chance
        return "Legendary"

# Simulate 1000 loot drops
results = [determine_loot_rarity() for _ in range(1000)]
for rarity in ["Common", "Uncommon", "Rare", "Epic", "Legendary"]:
    count = results.count(rarity)
    print(f"{rarity}: {count} ({count/10:.1f}%)")

3. random.uniform() - Random Floats (Custom Range)

Use Case: Generate random floats within a specific range (cleaner than scaling random.random()).

Syntax: random.uniform(a, b) - Returns float a <= N <= b (or b <= N <= a if b < a)

Example:

import random

# Random temperature between 20.0°C and 30.0°C
temperature = random.uniform(20.0, 30.0)
print(f"Temperature: {temperature:.2f}°C")  # e.g., 24.73°C

# Random price between $9.99 and $99.99
price = random.uniform(9.99, 99.99)
print(f"Price: ${price:.2f}")  # e.g., $47.82

Comparison - random() vs uniform():

# Method 1: Using random()
value1 = 5.0 + (10.0 - 5.0) * random.random()

# Method 2: Using uniform() - cleaner!
value2 = random.uniform(5.0, 10.0)

# Both produce same result, but uniform() is more readable

Practical Application - Generate Test Data:

import random
from datetime import datetime, timedelta

def generate_sensor_data(count=100):
    """Generate realistic sensor readings"""
    data = []
    base_time = datetime.now()

    for i in range(count):
        timestamp = base_time + timedelta(minutes=i)
        temperature = random.uniform(18.0, 28.0)  # °C
        humidity = random.uniform(30.0, 70.0)     # %
        pressure = random.uniform(980.0, 1020.0)  # hPa

        data.append({
            'timestamp': timestamp,
            'temperature': round(temperature, 2),
            'humidity': round(humidity, 2),
            'pressure': round(pressure, 2)
        })

    return data

# Generate test data
sensor_data = generate_sensor_data(10)
for reading in sensor_data[:3]:  # Show first 3
    print(reading)

4. random.choice() - Select Random Element

Use Case: Pick one random element from a sequence (list, tuple, string).

Syntax: random.choice(sequence) - Returns one random element

Basic Example:

import random

# Random element from list
colors = ['red', 'blue', 'green', 'yellow', 'purple']
selected = random.choice(colors)
print(f"Selected color: {selected}")  # e.g., 'green'

# Random character from string
password_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
char = random.choice(password_chars)
print(f"Random character: {char}")  # e.g., 'K'

# Random selection from tuple
directions = ('north', 'south', 'east', 'west')
direction = random.choice(directions)
print(f"Go {direction}")  # e.g., 'Go east'

Error Handling:

import random

empty_list = []
try:
    result = random.choice(empty_list)
except IndexError:
    print("Error: Cannot choose from empty sequence")

Practical Application - Random NPC Dialogue:

def get_npc_greeting():
    """Return random NPC greeting"""
    greetings = [
        "Hello, traveler!",
        "Greetings, adventurer.",
        "Well met, friend.",
        "What brings you here?",
        "Safe travels!",
    ]
    return random.choice(greetings)

# Use in game
for _ in range(3):
    print(get_npc_greeting())

Practical Application - A/B Test Assignment:

def assign_ab_test_variant(user_id):
    """Assign user to A/B test variant"""
    variants = ['control', 'variant_a', 'variant_b']

    # Set seed based on user_id for consistency
    random.seed(user_id)
    variant = random.choice(variants)
    random.seed()  # Reset seed

    return variant

# Same user always gets same variant
print(assign_ab_test_variant(12345))  # Always returns same variant for user 12345
print(assign_ab_test_variant(12345))  # Same result
print(assign_ab_test_variant(67890))  # Different user, different variant

5. random.choices() - Multiple Elements (With Replacement)

Use Case: Select multiple random elements, allowing duplicates (sampling with replacement).

Syntax: random.choices(sequence, weights=None, k=1)

Basic Example:

import random

colors = ['red', 'blue', 'green']

# Pick 5 colors (duplicates allowed)
selection = random.choices(colors, k=5)
print(selection)  # e.g., ['red', 'blue', 'red', 'red', 'green']

Weighted Random Selection:

import random

# Weighted loot drops
items = ['Common', 'Uncommon', 'Rare', 'Legendary']
weights = [50, 30, 15, 5]  # Probabilities (don't need to sum to 100)

# Generate 100 loot drops
drops = random.choices(items, weights=weights, k=100)

# Count results
for item in items:
    count = drops.count(item)
    print(f"{item}: {count}%")
# Output close to: Common: 50%, Uncommon: 30%, Rare: 15%, Legendary: 5%

Practical Application - Weighted Survey Responses:

def simulate_survey(num_responses=1000):
    """Simulate survey with realistic distribution"""
    responses = ['Very Satisfied', 'Satisfied', 'Neutral', 'Dissatisfied', 'Very Dissatisfied']

    # Realistic distribution (most people satisfied)
    weights = [25, 40, 20, 10, 5]

    results = random.choices(responses, weights=weights, k=num_responses)

    # Calculate percentages
    for response in responses:
        count = results.count(response)
        percentage = (count / num_responses) * 100
        print(f"{response}: {percentage:.1f}%")

simulate_survey(1000)

choices() vs choice():

import random

colors = ['red', 'blue', 'green']

# choice() - single element
single = random.choice(colors)  # 'blue'

# choices() - multiple elements (even just 1)
multiple = random.choices(colors, k=1)  # ['blue'] - returns list!

# choices() with k=5
five = random.choices(colors, k=5)  # ['red', 'blue', 'red', 'green', 'red']

Advanced Random Number Generation

6. random.sample() - Unique Random Elements (No Duplicates)

Use Case: Select multiple random elements without replacement (all unique).

Syntax: random.sample(sequence, k) - Returns list of k unique elements

Key Difference from choices():
- choices(): Allows duplicates (with replacement)
- sample(): No duplicates (without replacement)

Basic Example:

import random

numbers = list(range(1, 101))  # 1-100

# Pick 10 unique numbers
unique_numbers = random.sample(numbers, k=10)
print(f"Unique: {sorted(unique_numbers)}")
# e.g., [7, 14, 23, 31, 42, 58, 67, 79, 85, 93] - all different

# Compare with choices (allows duplicates)
with_duplicates = random.choices(numbers, k=10)
print(f"With duplicates: {sorted(with_duplicates)}")
# e.g., [7, 14, 23, 23, 42, 42, 58, 67, 79, 85] - may have duplicates

Error Handling:

import random

numbers = [1, 2, 3, 4, 5]

try:
    # ❌ Can't sample more elements than available
    result = random.sample(numbers, k=10)
except ValueError as e:
    print(f"Error: {e}")
    # Output: Error: Sample larger than population or is negative

Practical Application - Lottery Number Generator:

def generate_powerball():
    """Generate Powerball lottery numbers"""
    # 5 unique numbers from 1-69
    white_balls = random.sample(range(1, 70), k=5)
    white_balls.sort()

    # 1 Powerball from 1-26
    powerball = random.randint(1, 26)

    return white_balls, powerball

# Generate ticket
white, red = generate_powerball()
print(f"Numbers: {white}")
print(f"Powerball: {red}")
# Output: Numbers: [7, 14, 23, 42, 58]
#         Powerball: 12

Practical Application - Random Sampling for Testing:

def select_test_users(all_users, sample_size=100):
    """Select random users for A/B testing"""
    if sample_size > len(all_users):
        print(f"Warning: Sample size {sample_size} > population {len(all_users)}")
        sample_size = len(all_users)

    test_group = random.sample(all_users, k=sample_size)
    return test_group

# Example usage
all_users = [f"user_{i}" for i in range(1, 10001)]  # 10,000 users
test_users = select_test_users(all_users, sample_size=500)
print(f"Selected {len(test_users)} users for testing")
print(f"First 5: {test_users[:5]}")

Performance Consideration:

import random
import time

# Efficient: Direct sampling
start = time.time()
sample1 = random.sample(range(1, 1000001), k=10000)
print(f"Direct sample: {time.time() - start:.4f}s")

# Inefficient: Manual unique selection
start = time.time()
sample2 = []
numbers = list(range(1, 1000001))
while len(sample2) < 10000:
    num = random.choice(numbers)
    if num not in sample2:
        sample2.append(num)
print(f"Manual selection: {time.time() - start:.4f}s")

# Direct sampling is 100x faster!

For deep dive into unique number generation: No-Repeat Random Number Generator - 5 Implementation Methods

7. random.shuffle() - Randomize List Order

Use Case: Shuffle a list in-place (modifies original list).

Syntax: random.shuffle(list) - Returns None (modifies list directly)

Basic Example:

import random

# Shuffle a deck of cards
deck = ['A♠', 'K♠', 'Q♠', 'J♠', '10♠', '9♠']
print(f"Original: {deck}")

random.shuffle(deck)
print(f"Shuffled: {deck}")
# e.g., ['Q♠', '9♠', 'A♠', 'J♠', '10♠', 'K♠']

# shuffle() returns None, modifies in-place
result = random.shuffle(deck)
print(f"Return value: {result}")  # None

Important: Modifies Original List:

import random

original = [1, 2, 3, 4, 5]

# ❌ Wrong - shuffle() returns None
shuffled = random.shuffle(original)
print(shuffled)  # None

# ✅ Correct - shuffle modifies in-place
numbers = [1, 2, 3, 4, 5]
random.shuffle(numbers)
print(numbers)  # [3, 1, 5, 2, 4] - modified

# ✅ If you need to keep original, copy first
import copy
original = [1, 2, 3, 4, 5]
shuffled = copy.copy(original)
random.shuffle(shuffled)
print(f"Original: {original}")  # [1, 2, 3, 4, 5]
print(f"Shuffled: {shuffled}")  # [3, 1, 5, 2, 4]

Practical Application - Shuffle Playlist:

def create_shuffled_playlist(songs):
    """Create shuffled playlist without modifying original"""
    import copy
    playlist = copy.copy(songs)
    random.shuffle(playlist)
    return playlist

original_songs = [
    "Song A", "Song B", "Song C", "Song D", "Song E"
]

shuffled = create_shuffled_playlist(original_songs)
print(f"Original order preserved: {original_songs}")
print(f"Shuffled playlist: {shuffled}")

Practical Application - Randomize Exam Questions:

def randomize_exam(questions):
    """Shuffle exam questions for each student"""
    import copy

    # Preserve original order
    randomized = copy.deepcopy(questions)

    # Shuffle questions
    random.shuffle(randomized)

    # Shuffle answer choices for each question
    for question in randomized:
        if 'choices' in question:
            random.shuffle(question['choices'])

    return randomized

exam_questions = [
    {
        'question': 'What is 2+2?',
        'choices': ['3', '4', '5', '6'],
        'answer': '4'
    },
    {
        'question': 'Capital of France?',
        'choices': ['London', 'Paris', 'Berlin', 'Madrid'],
        'answer': 'Paris'
    }
]

student_exam = randomize_exam(exam_questions)
for i, q in enumerate(student_exam, 1):
    print(f"{i}. {q['question']}")
    print(f"   Choices: {q['choices']}")

8. random.seed() - Reproducible Randomness

Use Case: Set the random number generator's seed for reproducible sequences.

Syntax: random.seed(a=None) - Initialize with seed value (None uses system time)

Why Use Seeds?

Reproducibility: Same seed = same "random" sequence
- Scientific experiments requiring reproducible results
- Debugging random behavior in code
- Unit testing with predictable outcomes
- Synchronized randomness across systems (multiplayer games)

Basic Example:

import random

# Set seed
random.seed(42)
print(random.randint(1, 100))  # Always 82 with seed 42
print(random.randint(1, 100))  # Always 15

# Reset with same seed - get same sequence
random.seed(42)
print(random.randint(1, 100))  # 82 again!
print(random.randint(1, 100))  # 15 again!

# Different seed = different sequence
random.seed(123)
print(random.randint(1, 100))  # Different number

Resetting to Time-Based Seed:

import random

# Set specific seed
random.seed(42)
print(random.random())  # 0.6394267984578837

# Reset to time-based (unpredictable)
random.seed()  # or random.seed(None)
print(random.random())  # Different each run

Practical Application - Reproducible Scientific Simulation:

def monte_carlo_pi_estimation(samples=1000000, seed=None):
    """Estimate Pi using Monte Carlo method"""
    if seed is not None:
        random.seed(seed)

    inside_circle = 0

    for _ in range(samples):
        x = random.random()
        y = random.random()
        if x*x + y*y <= 1:
            inside_circle += 1

    pi_estimate = 4 * inside_circle / samples
    return pi_estimate

# Reproducible results
print(monte_carlo_pi_estimation(seed=42))  # Always same result
print(monte_carlo_pi_estimation(seed=42))  # Identical

# Different seed = different result (still close to Pi)
print(monte_carlo_pi_estimation(seed=123))

Practical Application - Synchronized Multiplayer Game:

def generate_game_map(room_id, size=100):
    """Generate identical map for all players in room"""
    # Use room_id as seed so all clients generate same map
    random.seed(room_id)

    # Generate terrain
    terrain = []
    for i in range(size):
        for j in range(size):
            tile_type = random.choice(['grass', 'water', 'mountain', 'forest'])
            terrain.append((i, j, tile_type))

    # Reset seed for other random operations
    random.seed()

    return terrain

# All players in room 12345 get identical map
map1 = generate_game_map(room_id=12345, size=10)
map2 = generate_game_map(room_id=12345, size=10)
print(f"Maps identical: {map1 == map2}")  # True

Important Security Warning:

import random

# ❌ NEVER use predictable seeds for security
random.seed(1)
password = ''.join(random.choice('abcdefghijklmnopqrstuvwxyz') for _ in range(10))
print(password)  # Always 'kqhmvsjbxy' - predictable!

# ✅ Use secrets module for security (never seeded)
import secrets
secure_password = ''.join(secrets.choice('abcdefghijklmnopqrstuvwxyz') for _ in range(10))
print(secure_password)  # Unpredictable, different each time

🎯 Random Module Function Reference

Quick comparison of when to use each function:

Need Function Example
Integer in range randint(a, b) randint(1, 100)
Float 0-1 random() random()
Float in range uniform(a, b) uniform(5.0, 10.0)
One item from list choice(seq) choice(['a', 'b', 'c'])
Multiple items (duplicates OK) choices(seq, k=n) choices(colors, k=5)
Multiple unique items sample(seq, k=n) sample(range(100), k=10)
Shuffle list in-place shuffle(list) shuffle(deck)
Reproducible sequence seed(n) seed(42)

💡 Pro Tip: When in doubt, test your random generation visually first using our Random Number Generator tool, then implement the logic in Python.


Security-Safe Random Number Generation

When random Module is NOT Enough

The random module is not cryptographically secure. Its output can theoretically be predicted if an attacker observes enough values.

NEVER use random for:
- Password generation
- Security tokens
- Encryption keys
- Session IDs
- Authentication codes
- Any security-critical application

Use secrets module instead (Python 3.6+):

The secrets Module

Import:

import secrets

Key Functions:

1. secrets.randbelow(n) - Random integer 0 to n-1:

import secrets

# Secure random number 0-99
secure_num = secrets.randbelow(100)
print(secure_num)

# Dice roll
dice = secrets.randbelow(6) + 1  # 1-6
print(f"Secure dice roll: {dice}")

2. secrets.choice(seq) - Secure random choice:

import secrets

# Secure random selection
colors = ['red', 'blue', 'green']
secure_choice = secrets.choice(colors)
print(secure_choice)

3. secrets.token_hex(nbytes) - Random hex string:

import secrets

# 32-character hex token (16 bytes)
token = secrets.token_hex(16)
print(f"Token: {token}")
# Output: Token: 4a7d8f3e9c2b1a6f8d5e3c2a1f9b8e7d

4. secrets.token_urlsafe(nbytes) - URL-safe token:

import secrets

# URL-safe token
session_id = secrets.token_urlsafe(32)
print(f"Session ID: {session_id}")
# Output: Session ID: x7Kf9Qm2Ln5Rp8Vt3Yw6Zb1Cd4Hj0Nk

Practical Application - Secure Password Generation:

import secrets
import string

def generate_secure_password(length=16):
    """Generate cryptographically secure password"""
    alphabet = string.ascii_letters + string.digits + string.punctuation
    password = ''.join(secrets.choice(alphabet) for _ in range(length))
    return password

# Generate 5 passwords
for i in range(5):
    pwd = generate_secure_password(20)
    print(f"Password {i+1}: {pwd}")

Practical Application - Secure OTP Generation:

import secrets

def generate_otp(digits=6):
    """Generate secure one-time password"""
    otp = ''.join(str(secrets.randbelow(10)) for _ in range(digits))
    return otp

# Generate OTP
otp_code = generate_otp(6)
print(f"Your OTP: {otp_code}")
# Output: Your OTP: 749382

Comparison - random vs secrets:

import random
import secrets

# ❌ INSECURE - predictable
random.seed(1)
insecure = random.randint(1000, 9999)
print(f"Insecure PIN: {insecure}")  # Always 2508 with seed 1

# ✅ SECURE - unpredictable
secure = secrets.randbelow(9000) + 1000
print(f"Secure PIN: {secure}")  # Different every time, unpredictable

Performance Note: secrets is slightly slower than random (10-20%), but security always trumps speed for sensitive operations.

For comprehensive security analysis: True Random vs Pseudo Random - Complete Security Analysis

Common Mistakes and How to Avoid Them

Mistake #1: Assuming randint() Upper Bound is Exclusive

import random

# ❌ Wrong assumption (from other languages)
number = random.randint(1, 101)  # Thinking upper bound is exclusive

# ✅ Correct - both bounds inclusive
number = random.randint(1, 100)  # Returns 1-100

Fix: Remember Python's randint(a, b) includes both a and b.

Mistake #2: Using shuffle() Return Value

import random

numbers = [1, 2, 3, 4, 5]

# ❌ Wrong - shuffle() returns None
shuffled = random.shuffle(numbers)
print(shuffled)  # None

# ✅ Correct - shuffle modifies in-place
numbers = [1, 2, 3, 4, 5]
random.shuffle(numbers)
print(numbers)  # [3, 1, 5, 2, 4]

Fix: shuffle() modifies the list in-place and returns None.

Mistake #3: Using random for Security

import random

# ❌ INSECURE password generation
password = ''.join(random.choice('abcdefghijklmnopqrstuvwxyz0123456789') for _ in range(12))

# ✅ SECURE password generation
import secrets
password = ''.join(secrets.choice('abcdefghijklmnopqrstuvwxyz0123456789') for _ in range(12))

Fix: Always use secrets module for security-critical randomness.

Mistake #4: Sampling More Than Population

import random

numbers = [1, 2, 3, 4, 5]

# ❌ Error - can't sample 10 from population of 5
try:
    result = random.sample(numbers, k=10)
except ValueError as e:
    print(f"Error: {e}")

# ✅ Correct - sample size <= population size
result = random.sample(numbers, k=5)

Fix: Ensure k <= len(sequence) when using sample().

Mistake #5: Not Resetting Seed After Using It

import random

def debug_function():
    random.seed(42)  # Set seed for debugging
    result = [random.randint(1, 10) for _ in range(5)]
    # ❌ Forgot to reset seed
    return result

# Now all subsequent random calls are predictable!
print(debug_function())
print(random.randint(1, 100))  # Still using seed 42!

# ✅ Correct - reset seed after use
def debug_function_fixed():
    random.seed(42)
    result = [random.randint(1, 10) for _ in range(5)]
    random.seed()  # Reset to time-based
    return result

Fix: Always call random.seed() (no argument) after using a specific seed.

Mistake #6: Inefficient Unique Number Generation

import random
import time

# ❌ Slow - manual uniqueness checking
start = time.time()
unique = []
while len(unique) < 10000:
    num = random.randint(1, 100000)
    if num not in unique:
        unique.append(num)
print(f"Slow method: {time.time() - start:.2f}s")

# ✅ Fast - use sample()
start = time.time()
unique = random.sample(range(1, 100001), k=10000)
print(f"Fast method: {time.time() - start:.2f}s")

# sample() is 100-1000x faster!

Fix: Always use random.sample() for unique random selection.

Performance Optimization Tips

Tip #1: Use List Comprehension for Batch Generation

import random
import time

# Slower - loop with append
start = time.time()
numbers = []
for _ in range(1000000):
    numbers.append(random.randint(1, 100))
print(f"Loop: {time.time() - start:.2f}s")

# Faster - list comprehension
start = time.time()
numbers = [random.randint(1, 100) for _ in range(1000000)]
print(f"Comprehension: {time.time() - start:.2f}s")

# ~10-20% faster with list comprehension

Tip #2: Use NumPy for Large Arrays

import random
import numpy as np
import time

# Python random - slower for large arrays
start = time.time()
python_array = [random.random() for _ in range(10000000)]
print(f"Python random: {time.time() - start:.2f}s")

# NumPy - much faster!
start = time.time()
numpy_array = np.random.random(10000000)
print(f"NumPy random: {time.time() - start:.2f}s")

# NumPy is 10-50x faster for large arrays!

Tip #3: Reuse random.Random() Instances for Thread Safety

import random
import threading

# Thread-safe random generation
def worker(worker_id, results, rng):
    """Each thread has its own RNG instance"""
    numbers = [rng.randint(1, 100) for _ in range(1000)]
    results[worker_id] = numbers

# Create separate RNG for each thread
threads = []
results = {}
for i in range(4):
    rng = random.Random()  # Separate instance per thread
    t = threading.Thread(target=worker, args=(i, results, rng))
    threads.append(t)
    t.start()

for t in threads:
    t.join()

print(f"Generated {sum(len(v) for v in results.values())} numbers across 4 threads")

Real-World Applications

Application #1: Monte Carlo Simulation

Use Case: Estimate complex probabilities through repeated random sampling.

import random

def estimate_pi(samples=1000000):
    """Estimate Pi using Monte Carlo method"""
    inside_circle = 0

    for _ in range(samples):
        x = random.random()
        y = random.random()

        # Check if point is inside unit circle
        if x*x + y*y <= 1:
            inside_circle += 1

    # Pi ≈ 4 * (points inside circle / total points)
    pi_estimate = 4 * inside_circle / samples
    return pi_estimate

# Estimate Pi
pi = estimate_pi(10000000)
print(f"Pi estimate: {pi}")
print(f"Actual Pi: 3.14159265359")
print(f"Error: {abs(pi - 3.14159265359):.10f}")

Application #2: Game Development - Procedural Generation

import random

class DungeonGenerator:
    def __init__(self, width=50, height=50, room_count=10, seed=None):
        self.width = width
        self.height = height
        self.room_count = room_count

        if seed:
            random.seed(seed)

        self.map = self.generate()

    def generate(self):
        """Generate random dungeon layout"""
        dungeon = [['#' for _ in range(self.width)] for _ in range(self.height)]

        rooms = []
        for _ in range(self.room_count):
            # Random room size and position
            room_width = random.randint(4, 10)
            room_height = random.randint(4, 10)
            x = random.randint(1, self.width - room_width - 1)
            y = random.randint(1, self.height - room_height - 1)

            # Carve out room
            for i in range(y, y + room_height):
                for j in range(x, x + room_width):
                    dungeon[i][j] = '.'

            rooms.append((x + room_width // 2, y + room_height // 2))

        # Connect rooms with corridors
        for i in range(len(rooms) - 1):
            x1, y1 = rooms[i]
            x2, y2 = rooms[i + 1]

            # Horizontal corridor
            for x in range(min(x1, x2), max(x1, x2) + 1):
                dungeon[y1][x] = '.'

            # Vertical corridor
            for y in range(min(y1, y2), max(y1, y2) + 1):
                dungeon[y][x2] = '.'

        return dungeon

    def display(self, sample_size=20):
        """Display portion of dungeon"""
        for row in self.map[:sample_size]:
            print(''.join(row[:sample_size]))

# Generate dungeon
dungeon = DungeonGenerator(seed=42)
dungeon.display()

Application #3: Data Science - Random Sampling

import random

def stratified_sample(data, strata_column, sample_size):
    """Perform stratified random sampling"""
    # Group data by strata
    strata = {}
    for item in data:
        stratum = item[strata_column]
        if stratum not in strata:
            strata[stratum] = []
        strata[stratum].append(item)

    # Calculate sample size per stratum (proportional)
    total_size = len(data)
    samples = []

    for stratum, items in strata.items():
        stratum_sample_size = int((len(items) / total_size) * sample_size)
        stratum_sample = random.sample(items, k=min(stratum_sample_size, len(items)))
        samples.extend(stratum_sample)

    return samples

# Example data
customers = [
    {'id': i, 'age_group': random.choice(['18-25', '26-35', '36-50', '51+'])}
    for i in range(10000)
]

# Stratified sample by age group
sample = stratified_sample(customers, 'age_group', sample_size=500)
print(f"Sampled {len(sample)} customers")

# Verify stratification
for group in ['18-25', '26-35', '36-50', '51+']:
    count = sum(1 for c in sample if c['age_group'] == group)
    print(f"{group}: {count} ({count/len(sample)*100:.1f}%)")

Application #4: Testing - Generate Realistic Test Data

import random
from datetime import datetime, timedelta

class TestDataGenerator:
    def __init__(self):
        self.first_names = ['John', 'Jane', 'Bob', 'Alice', 'Charlie', 'Diana']
        self.last_names = ['Smith', 'Johnson', 'Williams', 'Brown', 'Jones']
        self.domains = ['gmail.com', 'yahoo.com', 'outlook.com', 'example.com']

    def generate_user(self):
        """Generate realistic user data"""
        first_name = random.choice(self.first_names)
        last_name = random.choice(self.last_names)

        return {
            'id': random.randint(10000, 99999),
            'first_name': first_name,
            'last_name': last_name,
            'email': f"{first_name.lower()}.{last_name.lower()}@{random.choice(self.domains)}",
            'age': random.randint(18, 80),
            'join_date': self.random_date(datetime(2020, 1, 1), datetime.now()),
            'is_active': random.choice([True, True, True, False]),  # 75% active
            'balance': round(random.uniform(0, 10000), 2)
        }

    def random_date(self, start_date, end_date):
        """Generate random date between start and end"""
        time_between = end_date - start_date
        days_between = time_between.days
        random_days = random.randint(0, days_between)
        return start_date + timedelta(days=random_days)

    def generate_users(self, count=100):
        """Generate multiple users"""
        return [self.generate_user() for _ in range(count)]

# Generate test data
generator = TestDataGenerator()
test_users = generator.generate_users(10)

for user in test_users[:3]:
    print(user)

Explore more practical applications: Lottery Number Generator - 7 Application Scenarios

Python Random Number FAQ

Q1: What's the difference between random.randint() and random.randrange()?

Short Answer: randint(a, b) includes both a and b (inclusive). randrange(a, b) includes a but excludes b (like range()).

Detailed Comparison:

import random

# randint(a, b) - both inclusive
for _ in range(5):
    print(random.randint(1, 10))  # Can output 1, 2, 3... 9, or 10

# randrange(a, b) - b is exclusive (like range)
for _ in range(5):
    print(random.randrange(1, 10))  # Can output 1, 2, 3... 9 (never 10)

# Equivalent to randint
print(random.randrange(1, 11))  # Same as randint(1, 10)

When to Use Each:
- Use randint(a, b): When you want inclusive bounds (most common)
- Use randrange(a, b): When you're used to range() behavior or need step parameter

randrange() with step:

# Generate random even number between 0-100
even = random.randrange(0, 101, 2)  # 0, 2, 4, 6... 100
print(even)

# Generate random multiple of 5
multiple_of_5 = random.randrange(0, 101, 5)  # 0, 5, 10, 15... 100
print(multiple_of_5)

Q2: How do I generate random numbers without numpy?

Answer: The built-in random module is sufficient for most use cases. Use NumPy only when:
- Generating very large arrays (millions of numbers)
- Need specific statistical distributions
- Performance is critical

Pure Python Examples:

import random

# Large list of random numbers - pure Python
numbers = [random.randint(1, 100) for _ in range(1000000)]

# Random floats
floats = [random.random() for _ in range(100000)]

# Normal distribution (pure Python)
def normal_distribution_sample(mean=0, std_dev=1):
    """Box-Muller transform for normal distribution"""
    import math
    u1 = random.random()
    u2 = random.random()
    z0 = math.sqrt(-2 * math.log(u1)) * math.cos(2 * math.pi * u2)
    return mean + z0 * std_dev

# Generate 1000 samples from normal distribution
normal_samples = [normal_distribution_sample(100, 15) for _ in range(1000)]

When NumPy is Better:

import numpy as np
import random
import time

# Python random - slower
start = time.time()
python_list = [random.random() for _ in range(10000000)]
print(f"Python: {time.time() - start:.2f}s")

# NumPy - much faster
start = time.time()
numpy_array = np.random.random(10000000)
print(f"NumPy: {time.time() - start:.2f}s")

# NumPy is 10-50x faster for large datasets

Q3: Can I generate truly random numbers in Python?

Short Answer: The random module generates pseudo-random numbers (deterministic algorithm). For true randomness, use secrets module (uses OS-level entropy) or external services like Random.org.

Pseudo-Random (random module):

import random

# Pseudo-random - deterministic with same seed
random.seed(42)
print([random.randint(1, 100) for _ in range(5)])
# Always: [82, 15, 74, 2, 37] with seed 42

random.seed(42)
print([random.randint(1, 100) for _ in range(5)])
# Identical: [82, 15, 74, 2, 37]

Cryptographically Secure (secrets module):

import secrets

# Uses OS entropy sources (true randomness)
print([secrets.randbelow(100) for _ in range(5)])
# Different every time, unpredictable

True Random (External Source):

import requests

# Random.org API (true random from atmospheric noise)
response = requests.get('https://www.random.org/integers/?num=10&min=1&max=100&col=1&base=10&format=plain&rnd=new')
true_random_numbers = [int(x) for x in response.text.strip().split()]
print(true_random_numbers)

Bottom Line:
- General use: random module (pseudo-random is fine)
- Security: secrets module (cryptographically secure)
- Scientific research: Consider true random sources or secrets

For deep dive: True Random vs Pseudo Random - Complete Analysis

Q4: How do I make my random numbers reproducible for testing?

Answer: Use random.seed() with a fixed value.

Unit Test Example:

import random
import unittest

def process_random_data():
    """Function that uses randomness"""
    data = [random.randint(1, 100) for _ in range(10)]
    return sum(data) / len(data)

class TestRandomFunction(unittest.TestCase):
    def test_process_with_seed(self):
        """Test with reproducible random data"""
        random.seed(42)
        result1 = process_random_data()

        random.seed(42)
        result2 = process_random_data()

        # Same seed = identical results
        self.assertEqual(result1, result2)

    def test_average_in_expected_range(self):
        """Test average is reasonable"""
        random.seed(42)
        result = process_random_data()

        # Average should be around 50 for uniform 1-100
        self.assertGreater(result, 20)
        self.assertLess(result, 80)

# Run tests
unittest.main(argv=[''], exit=False)

Pytest Example:

import random
import pytest

@pytest.fixture(autouse=True)
def reset_random_seed():
    """Reset seed before each test"""
    random.seed(42)
    yield
    random.seed()  # Reset after test

def test_lottery_numbers():
    """Test lottery number generation"""
    numbers = random.sample(range(1, 50), k=6)

    # With seed 42, always get same numbers
    assert numbers == [32, 43, 4, 47, 7, 39]

def test_dice_roll():
    """Test dice roll"""
    roll = random.randint(1, 6)

    # With seed 42, first roll is always 5
    assert roll == 5

Important: Always reset seed after tests:

def setUp(self):
    random.seed(42)

def tearDown(self):
    random.seed()  # Reset to time-based

Q5: What's the fastest way to generate millions of random numbers?

Answer: Use NumPy for bulk generation, or optimize Python random with list comprehensions.

Performance Comparison:

import random
import numpy as np
import time

COUNT = 10000000

# Method 1: Python loop (slowest)
start = time.time()
numbers1 = []
for _ in range(COUNT):
    numbers1.append(random.randint(1, 100))
print(f"Python loop: {time.time() - start:.2f}s")

# Method 2: Python list comprehension (faster)
start = time.time()
numbers2 = [random.randint(1, 100) for _ in range(COUNT)]
print(f"Python comprehension: {time.time() - start:.2f}s")

# Method 3: NumPy (fastest)
start = time.time()
numbers3 = np.random.randint(1, 101, size=COUNT)
print(f"NumPy: {time.time() - start:.2f}s")

# Results (approximate):
# Python loop: 8.5s
# Python comprehension: 7.2s (15% faster)
# NumPy: 0.3s (25x faster!)

Optimization Tips:

1. Use NumPy for large arrays:

import numpy as np

# Generate 10 million random integers
large_array = np.random.randint(1, 101, size=10000000)

2. Pre-allocate and fill (if you must use Python):

import random

# Pre-allocate list
count = 1000000
numbers = [0] * count

# Fill in-place
for i in range(count):
    numbers[i] = random.randint(1, 100)

3. Use appropriate data structures:

import random
import array

# array module is faster than list for homogeneous data
numbers = array.array('i', (random.randint(1, 100) for _ in range(1000000)))

Conclusion: Mastering Python Random Number Generation

You've now learned the complete toolkit for random number generation in Python, from basics to advanced techniques.

Key Takeaways

Core Functions:
- randint(a, b): Integer in range (both inclusive)
- random(): Float 0.0-1.0
- choice(seq): Pick one element
- sample(seq, k): Pick k unique elements
- shuffle(list): Randomize list order
- seed(n): Reproducible sequences

Security:
- Never use random for security - use secrets module
- secrets.choice(), secrets.token_hex() for secure randomness
- Passwords, tokens, keys require cryptographically secure RNGs

Performance:
- List comprehensions 15-20% faster than loops
- NumPy 10-50x faster for large arrays
- Use sample() not manual loops for unique numbers

Best Practices:
- Use seeds only for reproducibility, never security
- Reset seeds after debugging: random.seed()
- Check k <= len(seq) before calling sample()
- Remember shuffle() modifies in-place, returns None

Next Steps

Want to practice? Try our Random Number Generator tool to visualize concepts before coding.

Continue Learning:
- Random Number Generator Complete Guide - broader concepts
- No-Repeat Random Number Generator - unique number techniques
- True Random vs Pseudo Random - security deep dive
- Excel Random Number Tutorial - non-programming alternative

Explore Related Tools:
- Password Generator - see secrets in action
- Unit Converter - convert random values
- Browse all Developer Tools

Practice Projects:
1. Build a lottery number generator
2. Create a card game with shuffling
3. Implement Monte Carlo simulation
4. Generate realistic test data
5. Build a password generator with strength meter

Remember: The right random function for the job matters. randint() for integers, sample() for unique selection, secrets for security. Master these, and you'll handle any randomness challenge Python throws at you.

Happy coding! 🐍🎲


References

  1. Python Software Foundation, random — Generate pseudo-random numbers (Python 3.12 Documentation, 2024)
  2. Python Software Foundation, secrets — Generate secure random numbers (Python 3.12 Documentation, 2024)
  3. NumPy Documentation, numpy.random (NumPy 1.26 Documentation, 2024)
  4. Matsumoto & Nishimura, Mersenne Twister: A 623-dimensionally equidistributed uniform pseudo-random number generator (1998)
  5. Donald E. Knuth, The Art of Computer Programming, Volume 2: Seminumerical Algorithms (3rd Edition, 1997)
  6. NIST, Special Publication 800-90A: Recommendation for Random Number Generation (2015)
  7. Stack Overflow, Python Random Module Questions (2024) - Community Q&A
  8. Real Python, Generating Random Data in Python (2024) - Tutorial resource
  9. Python Testing Best Practices, Testing with Random Data (2024)
  10. Tool Master, Random Number Generator Complete Guide (2025)