Foundation Guide

Python Standard Library: random

📄 28 Pages 🎯 Random numbers, choices, shuffling

What this page does

Introduces the random module and what you will learn.

Where this fits

This is the starting point. You should know basic Python (variables, lists, functions) before starting.

Explanation

The random module generates pseudo-random numbers and makes random selections. By the end of this guide, you will know how to:

  • Generate random numbers — Integers and floats in any range
  • Make random choices — Pick items from lists
  • Sample data — Select multiple items with or without replacement
  • Shuffle sequences — Randomize the order of lists
  • Control randomness — Use seeds for reproducible results
The random module is built into Python. No installation required.

Why this matters

Randomness is everywhere: games, simulations, testing, data sampling, password generation, and machine learning. The random module is your toolkit for all of it.

✓ Checkpoint

⚠ If something breaks here

Nothing to break yet. Move to Page 2.

What this page does

Sets up a Python file for practicing the random module.

Where this fits

Before writing code, create a workspace.

Code (this page)

mkdir ~/projects/random-practice
cd ~/projects/random-practice
touch random_basics.py

Explanation

These commands:

  • Create a folder called random-practice
  • Enter that folder
  • Create an empty Python file
From now on, you will write code in random_basics.py and run it with python3 random_basics.py.

Why this matters

Having a dedicated file lets you build up code incrementally and re-run to test changes.

✓ Checkpoint

⚠ If something breaks here

  • mkdir: File exists: The folder exists. Just cd into it
  • Permission denied: Check you're in a writable location

What this page does

Imports the random module into your Python file.

Where this fits

Your file is ready. Now add the import.

Code (this page)

import random

print("random module imported successfully")

Explanation

Open random_basics.py in VS Code and add this code.

import random loads the module. Unlike NumPy, we typically don't use an alias — random is already short.

Run it:

python3 random_basics.py

Output:

random module imported successfully

Why this matters

This import line starts every script that uses randomness. The random module is always available — no pip install needed.

✓ Checkpoint

⚠ If something breaks here

  • ModuleNotFoundError: This shouldn't happen — random is built-in. Check spelling.
  • Typo: It's random not Random

What this page does

Generates a random float between 0 and 1.

Where this fits

Module imported. Now generate your first random number.

Code (this page)

import random

value = random.random() print("Random float:", value)

# Generate several to see variation for i in range(5): print(random.random())

Explanation

Run the file (your numbers will differ):

Random float: 0.7234567891234567
0.12345678912345678
0.89012345678901234
0.45678901234567890
0.23456789012345678
0.67890123456789012

random.random() returns a float where:

  • Minimum: 0.0 (inclusive)
  • Maximum: 1.0 (exclusive — never exactly 1.0)
Every call produces a different number.

Why this matters

This is the foundation of all other random functions. Many random operations are built by scaling this 0-1 value.

✓ Checkpoint

⚠ If something breaks here

  • Same number every time: You might have random.seed() somewhere. Check your code.

What this page does

Generates a random integer in a specified range.

Where this fits

You made random floats. Now make random integers.

Code (this page)

import random

# Random integer between 1 and 10 (inclusive) dice = random.randint(1, 6) print("Dice roll:", dice)

# Generate several print("Five dice rolls:") for i in range(5): print(random.randint(1, 6))

Explanation

Run the file:

Dice roll: 4
Five dice rolls:
2
6
1
3
5

random.randint(a, b) returns an integer where:

  • Minimum: a (inclusive)
  • Maximum: b (inclusive) — unlike most Python ranges!
This is perfect for dice, card games, or any discrete random selection.

Why this matters

Integer randomness is more common than float randomness in practice. Dice rolls, random indices, ID generation — all use integers.

✓ Checkpoint

⚠ If something breaks here

  • TypeError: Both arguments must be integers, not floats

What this page does

Generates a random float in a specified range.

Where this fits

randint for integers. Now uniform for floats.

Code (this page)

import random

# Random float between 1.0 and 10.0 value = random.uniform(1.0, 10.0) print("Random float 1-10:", value)

# Temperature simulation (60-80 degrees) temp = random.uniform(60.0, 80.0) print(f"Temperature: {temp:.1f}°F")

# Can go negative value = random.uniform(-5.0, 5.0) print("Random -5 to 5:", value)

Explanation

Run the file:

Random float 1-10: 7.234567891234567
Temperature: 73.4°F
Random -5 to 5: -2.345678901234567

random.uniform(a, b) returns a float where:

  • Minimum: a
  • Maximum: b
  • Both endpoints can potentially be included (implementation detail)
Unlike random.random() which only gives 0-1, uniform scales to any range.

Why this matters

Simulations need specific ranges. Temperature, coordinates, percentages, prices — all need scaled random floats.

✓ Checkpoint

⚠ If something breaks here

  • Nothing should break. Arguments can be int or float.

What this page does

Picks a random item from a list.

Where this fits

You generated numbers. Now select from existing data.

Code (this page)

import random

colors = ["red", "green", "blue", "yellow", "purple"] picked = random.choice(colors) print("Picked color:", picked)

# Works with any sequence letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" letter = random.choice(letters) print("Random letter:", letter)

# Numbers too numbers = [10, 20, 30, 40, 50] num = random.choice(numbers) print("Picked number:", num)

Explanation

Run the file:

Picked color: blue
Random letter: M
Picked number: 30

random.choice(sequence):

  • Takes any sequence (list, string, tuple)
  • Returns one randomly selected item
  • Each item has equal probability

Why this matters

Selecting random items is extremely common: random card from deck, random player goes first, random test case, random quote of the day.

✓ Checkpoint

⚠ If something breaks here

  • IndexError: Cannot choose from an empty sequence: The list is empty
  • TypeError: Argument must be a sequence, not a single value

What this page does

Picks multiple random items WITH replacement.

Where this fits

choice() picks one. choices() picks many.

Code (this page)

import random

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

# Pick 5 colors (can repeat) picks = random.choices(colors, k=5) print("5 picks with replacement:", picks)

# Simulate 10 coin flips coin = ["heads", "tails"] flips = random.choices(coin, k=10) print("10 coin flips:", flips)

Explanation

Run the file:

5 picks with replacement: ['blue', 'red', 'blue', 'green', 'blue']
10 coin flips: ['heads', 'tails', 'heads', 'heads', 'tails', 'heads', 'tails', 'tails', 'heads', 'heads']

random.choices(sequence, k=n):

  • Returns a list of k random items
  • WITH replacement — same item can appear multiple times
  • Original list is not modified
Notice "blue" appeared 3 times in the first example. That's replacement.

Why this matters

Simulations often need repeated sampling. Coin flips, dice rolls, bootstrap sampling — all need replacement.

✓ Checkpoint

⚠ If something breaks here

  • Forgot k=: You need random.choices(list, k=5) not random.choices(list, 5)

What this page does

Shows how to make some choices more likely than others.

Where this fits

Equal probability is default. Weights customize it.

Code (this page)

import random

# Weighted coin (70% heads, 30% tails) coin = ["heads", "tails"] weights = [70, 30] flips = random.choices(coin, weights=weights, k=10) print("Biased coin:", flips)

# Rarity system (common, rare, legendary) items = ["common", "rare", "legendary"] weights = [80, 18, 2] # 80% common, 18% rare, 2% legendary drops = random.choices(items, weights=weights, k=20) print("Loot drops:", drops) print("Legendaries:", drops.count("legendary"))

Explanation

Run the file:

Biased coin: ['heads', 'heads', 'tails', 'heads', 'heads', 'heads', 'heads', 'tails', 'heads', 'heads']
Loot drops: ['common', 'common', 'rare', 'common', 'common', 'common', 'rare', 'common', 'common', 'common', 'common', 'rare', 'common', 'common', 'common', 'common', 'common', 'rare', 'common', 'common']
Legendaries: 0

weights parameter:

  • Higher weight = more likely to be picked
  • Weights don't need to sum to 100 — they're relative
  • [70, 30] is the same as [7, 3] or [0.7, 0.3]

Why this matters

Real systems aren't uniform. Loot drops, ad selection, A/B testing, genetic algorithms — all use weighted randomness.

✓ Checkpoint

⚠ If something breaks here

  • ValueError: Number of weights must match number of items

What this page does

Picks multiple random items WITHOUT replacement.

Where this fits

choices() allows repeats. sample() doesn't.

Code (this page)

import random

# Pick 3 unique colors colors = ["red", "green", "blue", "yellow", "purple"] picks = random.sample(colors, k=3) print("3 unique colors:", picks)

# Deal 5 cards from a deck deck = list(range(1, 53)) # Cards 1-52 hand = random.sample(deck, k=5) print("5-card hand:", hand)

# Lottery numbers (6 unique from 1-49) numbers = list(range(1, 50)) lottery = random.sample(numbers, k=6) print("Lottery numbers:", sorted(lottery))

Explanation

Run the file:

3 unique colors: ['purple', 'red', 'green']
5-card hand: [23, 7, 45, 12, 38]
Lottery numbers: [5, 17, 23, 31, 42, 48]

random.sample(sequence, k=n):

  • Returns a list of k unique items
  • WITHOUT replacement — no repeats
  • Original list is not modified
  • k cannot exceed the length of the sequence

Why this matters

Card games, lottery systems, random team assignments, test question selection — anywhere you need unique picks.

✓ Checkpoint

⚠ If something breaks here

  • ValueError: Sample larger than population: You asked for more items than exist

What this page does

Randomizes the order of a list in place.

Where this fits

Selecting items is done. Now rearrange them.

Code (this page)

import random

# Shuffle a deck of cards deck = list(range(1, 53)) print("Before shuffle:", deck[:10], "...")

random.shuffle(deck) print("After shuffle:", deck[:10], "...")

# Shuffle names for random order names = ["Alice", "Bob", "Charlie", "Diana"] print("Original:", names)

random.shuffle(names) print("Shuffled:", names)

Explanation

Run the file:

Before shuffle: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] ...
After shuffle: [34, 12, 47, 3, 28, 51, 9, 41, 16, 22] ...
Original: ['Alice', 'Bob', 'Charlie', 'Diana']
Shuffled: ['Charlie', 'Alice', 'Diana', 'Bob']

random.shuffle(list):

  • Modifies the list IN PLACE
  • Returns None (not the shuffled list)
  • Only works on mutable sequences (lists, not tuples or strings)

Why this matters

Card games need shuffling. Randomized quizzes need shuffled questions. Playlists need shuffle mode. This is it.

✓ Checkpoint

⚠ If something breaks here

  • TypeError: 'str' object does not support item assignment: Can't shuffle strings. Convert to list first.
  • Assigned result to variable and got None: Shuffle returns nothing. The list itself is changed.

What this page does

Shows how to shuffle while keeping the original intact.

Where this fits

Sometimes you need both: original order and shuffled order.

Code (this page)

import random

# Method 1: Use sample with full length original = [1, 2, 3, 4, 5] shuffled = random.sample(original, k=len(original)) print("Original:", original) print("Shuffled:", shuffled)

# Method 2: Copy then shuffle original = ["A", "B", "C", "D", "E"] shuffled = original.copy() random.shuffle(shuffled) print("Original:", original) print("Shuffled:", shuffled)

Explanation

Run the file:

Original: [1, 2, 3, 4, 5]
Shuffled: [3, 1, 5, 2, 4]
Original: ['A', 'B', 'C', 'D', 'E']
Shuffled: ['D', 'A', 'E', 'C', 'B']

Two approaches:

  • random.sample(list, k=len(list)) — returns a new shuffled list
  • list.copy() then random.shuffle() — explicit copy first
Both preserve the original. Method 1 is more concise.

Why this matters

Preserving original order is important for comparison, debugging, or showing "before and after."

✓ Checkpoint

⚠ If something breaks here

Nothing should break. Both methods are safe.

What this page does

Makes random output reproducible.

Where this fits

Random is random. But sometimes you need predictable random.

Code (this page)

import random

# Without seed: different every run print("Without seed:") print(random.randint(1, 100)) print(random.randint(1, 100))

# With seed: same every run print("\nWith seed(42):") random.seed(42) print(random.randint(1, 100)) print(random.randint(1, 100))

# Reset seed to get same sequence again print("\nSeed(42) again:") random.seed(42) print(random.randint(1, 100)) print(random.randint(1, 100))

Explanation

Run the file multiple times:

Without seed:
73
28

With seed(42): 82 15

Seed(42) again: 82 15

The "without seed" numbers change each run. The "with seed" numbers are ALWAYS 82 and 15 (when seed is 42).

random.seed(value):

  • Initializes the random number generator
  • Same seed = same sequence of "random" numbers
  • Any hashable value works (integers are common)

Why this matters

Testing needs reproducibility. "This bug happens with seed 42" is debuggable. Scientific experiments need reproducible results.

✓ Checkpoint

⚠ If something breaks here

  • Numbers differ from my example: Different Python versions may give different sequences. Consistency is within YOUR runs.

What this page does

Generates random integers with step control.

Where this fits

randint includes both endpoints. randrange works like range().

Code (this page)

import random

# Random from 0 to 9 (like range(10)) value = random.randrange(10) print("0-9:", value)

# Random from 5 to 14 (like range(5, 15)) value = random.randrange(5, 15) print("5-14:", value)

# Random even number 0-20 (like range(0, 21, 2)) value = random.randrange(0, 21, 2) print("Even 0-20:", value)

# Random multiple of 5 from 0-100 value = random.randrange(0, 101, 5) print("Multiple of 5:", value)

Explanation

Run the file:

0-9: 7
5-14: 11
Even 0-20: 14
Multiple of 5: 65

random.randrange(start, stop, step):

  • Works exactly like range() but returns one random value
  • Stop is EXCLUSIVE (unlike randint)
  • Step allows patterns (evens, odds, multiples)

Why this matters

When you need random values that follow a pattern (every 5th number, only evens), randrange is cleaner than filtering.

✓ Checkpoint

⚠ If something breaks here

  • ValueError: empty range: Your start/stop/step combination produces no valid values

What this page does

Generates random numbers following a normal (bell curve) distribution.

Where this fits

Uniform distribution gives equal probability. Gaussian is for natural variation.

Code (this page)

import random

# Human heights (mean=170cm, std=10cm) heights = [random.gauss(170, 10) for _ in range(10)] print("Heights (cm):") for h in heights: print(f" {h:.1f}")

# Test scores (mean=75, std=15) scores = [random.gauss(75, 15) for _ in range(10)] print("\nTest scores:") for s in scores: print(f" {s:.1f}")

Explanation

Run the file:

Heights (cm):
  168.3
  175.2
  162.7
  171.0
  ...

Test scores: 82.3 68.1 79.5 ...

random.gauss(mu, sigma):

  • mu = mean (center of bell curve)
  • sigma = standard deviation (spread)
  • ~68% of values fall within 1 sigma of mean
  • ~95% fall within 2 sigma
Real-world data (heights, scores, errors) often follows this distribution.

Why this matters

Uniform random is unrealistic for natural phenomena. Gaussian randomness models reality better for simulations.

✓ Checkpoint

⚠ If something breaks here

Nothing should break. Both arguments can be any number.

What this page does

Clarifies the difference between randint and randrange.

Where this fits

Both generate integers. Know which to use when.

Code (this page)

import random

random.seed(100)

# randint: both endpoints INCLUDED print("randint(1, 10) — includes 1 AND 10:") for _ in range(10): print(random.randint(1, 10), end=" ") print()

random.seed(100)

# randrange: stop is EXCLUDED (like range) print("\nrandrange(1, 11) — includes 1, excludes 11:") for _ in range(10): print(random.randrange(1, 11), end=" ") print()

Explanation

Run the file:

randint(1, 10) — includes 1 AND 10:
2 5 7 1 10 3 8 4 9 6

randrange(1, 11) — includes 1, excludes 11: 2 5 7 1 10 3 8 4 9 6

Both produce the same results because:

  • randint(1, 10) = 1 to 10 inclusive
  • randrange(1, 11) = 1 to 10 (11 excluded)
Rule of thumb:
  • Use randint for inclusive ranges (dice: 1-6)
  • Use randrange when working with indices or range-like logic

Why this matters

Off-by-one errors are common bugs. Knowing the exact behavior prevents them.

✓ Checkpoint

⚠ If something breaks here

Nothing should break. This is clarification.

What this page does

Builds a practical password generator using random.

Where this fits

Apply what you learned to a real-world task.

Code (this page)

import random
import string

def generate_password(length=12): # All possible characters chars = string.ascii_letters + string.digits + string.punctuation # Pick random characters password = ''.join(random.choices(chars, k=length)) return password

# Generate passwords print("Random passwords:") for i in range(5): print(f" {generate_password()}")

# Custom length print(f"\n20-character: {generate_password(20)}") print(f"8-character: {generate_password(8)}")

Explanation

Run the file:

Random passwords:
  kQ7#mP2!xL9@
  Nt5&hR8*wJ3$
  ...

20-character: Bx4#Lm9@Pq2!Wk7&Hn5$ 8-character: Jf3@Kp8!

Key techniques used:

  • string.ascii_letters = a-zA-Z
  • string.digits = 0-9
  • string.punctuation = !@#$% etc.
  • random.choices() = pick multiple characters
  • ''.join() = combine into string

Why this matters

Password generation is a real use case. This pattern applies to any "generate random string" task.

✓ Checkpoint

⚠ If something breaks here

  • ModuleNotFoundError: string: Add import string at the top

What this page does

Simulates rolling dice and counting outcomes.

Where this fits

Games are the classic use case for randomness.

Code (this page)

import random

def roll_dice(num_dice=2, sides=6): """Roll multiple dice and return the total.""" rolls = [random.randint(1, sides) for _ in range(num_dice)] return rolls, sum(rolls)

# Roll 2 six-sided dice rolls, total = roll_dice() print(f"Rolled: {rolls} = {total}")

# Simulate 1000 rolls and count 7s sevens = 0 for _ in range(1000): _, total = roll_dice() if total == 7: sevens += 1

print(f"\nIn 1000 rolls, got 7 exactly {sevens} times") print(f"That's {sevens/10:.1f}% (expected ~16.7%)")

Explanation

Run the file:

Rolled: [3, 4] = 7

In 1000 rolls, got 7 exactly 162 times That's 16.2% (expected ~16.7%)

The simulation confirms probability theory:

  • 7 is the most common sum with 2 dice
  • There are 6 ways to make 7 out of 36 possible outcomes
  • 6/36 = 16.67%

Why this matters

Simulations verify probability calculations and model real-world scenarios.

✓ Checkpoint

⚠ If something breaks here

Nothing should break. Experiment with different dice configurations.

What this page does

Simulates shuffling and dealing a deck of cards.

Where this fits

Cards combine shuffle, sample, and choice.

Code (this page)

import random

# Build a deck suits = ['♠', '♥', '♦', '♣'] ranks = ['A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K'] deck = [f"{rank}{suit}" for suit in suits for rank in ranks]

print(f"Deck has {len(deck)} cards")

# Shuffle random.shuffle(deck)

# Deal 5 cards to 2 players player1 = [deck.pop() for _ in range(5)] player2 = [deck.pop() for _ in range(5)]

print(f"\nPlayer 1: {player1}") print(f"Player 2: {player2}") print(f"Cards remaining: {len(deck)}")

Explanation

Run the file:

Deck has 52 cards

Player 1: ['7♦', 'K♠', '3♥', 'A♣', '9♦'] Player 2: ['Q♥', '5♠', '2♦', 'J♣', '8♥'] Cards remaining: 42

Key techniques:

  • List comprehension builds the deck
  • shuffle() randomizes in place
  • pop() removes and returns the last card (dealing)

Why this matters

Card games are a perfect model for "sample without replacement" problems.

✓ Checkpoint

⚠ If something breaks here

  • IndexError: pop from empty list: Dealt more cards than the deck contains

What this page does

Creates a lottery where some tickets have better odds.

Where this fits

Real lotteries often have tiered prizes with different odds.

Code (this page)

import random

def lottery_draw(): """Draw a prize with weighted probabilities.""" prizes = ["$1,000,000", "$10,000", "$1,000", "$100", "$10", "Nothing"] weights = [1, 10, 100, 1000, 5000, 93889] # Out of 100,000 return random.choices(prizes, weights=weights, k=1)[0]

# Run the lottery 10 times print("Lottery results:") results = {} for _ in range(10): prize = lottery_draw() print(f" {prize}") results[prize] = results.get(prize, 0) + 1

# Simulate 100,000 draws print("\n100,000 draw simulation:") results = {} for _ in range(100000): prize = lottery_draw() results[prize] = results.get(prize, 0) + 1

for prize, count in sorted(results.items(), key=lambda x: -x[1]): print(f" {prize}: {count} ({count/1000:.2f}%)")

Explanation

Run the file:

Lottery results:
  Nothing
  Nothing
  $10
  Nothing
  ...

100,000 draw simulation: Nothing: 93876 (93.88%) $10: 4987 (4.99%) $100: 1021 (1.02%) $1,000: 98 (0.10%) $10,000: 11 (0.01%) $1,000,000: 7 (0.01%)

The simulation matches the weights:

  • 93.889% chance of nothing
  • 5% chance of $10
  • Etc.

Why this matters

Understanding weighted probability is crucial for game design, simulations, and detecting unfair systems.

✓ Checkpoint

⚠ If something breaks here

Nothing should break. Try adjusting weights to change odds.

What this page does

Demonstrates sampling techniques for data analysis.

Where this fits

Data scientists use random sampling constantly.

Code (this page)

import random

# Simulated dataset: 1000 customer ages population = [random.randint(18, 80) for _ in range(1000)]

# Simple random sample sample = random.sample(population, k=50) print(f"Population mean: {sum(population)/len(population):.1f}") print(f"Sample mean (n=50): {sum(sample)/len(sample):.1f}")

# Multiple samples show variation print("\n10 different samples (n=50):") for i in range(10): s = random.sample(population, k=50) mean = sum(s)/len(s) print(f" Sample {i+1}: mean = {mean:.1f}")

Explanation

Run the file:

Population mean: 48.7
Sample mean (n=50): 47.2

10 different samples (n=50): Sample 1: mean = 49.3 Sample 2: mean = 46.8 Sample 3: mean = 50.1 ...

Key insight: Sample means cluster around the population mean but vary. Larger samples = less variation.

Why this matters

You can't always measure everything. Sampling lets you estimate population statistics from a subset.

✓ Checkpoint

⚠ If something breaks here

Nothing should break. Try different sample sizes.

What this page does

Creates a quiz with randomized question order.

Where this fits

Educational software often randomizes to prevent memorization of order.

Code (this page)

import random

questions = [ {"q": "What is 2 + 2?", "a": "4"}, {"q": "Capital of France?", "a": "Paris"}, {"q": "Largest planet?", "a": "Jupiter"}, {"q": "H2O is?", "a": "Water"}, {"q": "Year Python released?", "a": "1991"}, ]

def run_quiz(questions): # Shuffle without modifying original shuffled = random.sample(questions, k=len(questions)) score = 0 print("=== QUIZ ===\n") for i, item in enumerate(shuffled, 1): print(f"Q{i}: {item['q']}") answer = input("Your answer: ").strip() if answer.lower() == item['a'].lower(): print("✓ Correct!\n") score += 1 else: print(f"✗ Wrong. Answer: {item['a']}\n") print(f"Final score: {score}/{len(questions)}")

# Uncomment to run interactive quiz: # run_quiz(questions)

# Demo: show shuffled order print("Question order (shuffled):") shuffled = random.sample(questions, k=len(questions)) for i, item in enumerate(shuffled, 1): print(f" {i}. {item['q']}")

Explanation

Run the file:

Question order (shuffled):
  1. H2O is?
  2. Capital of France?
  3. Year Python released?
  4. Largest planet?
  5. What is 2 + 2?

Every run shows a different order. This prevents students from memorizing "question 3 is always about France."

Why this matters

Randomization in education, surveys, and testing ensures fairness and prevents pattern exploitation.

✓ Checkpoint

⚠ If something breaks here

Nothing should break. The interactive part is commented out.

What this page does

Generates random colors in different formats.

Where this fits

Graphics, data visualization, and UI design use random colors.

Code (this page)

import random

def random_hex_color(): """Generate random color as #RRGGBB""" r = random.randint(0, 255) g = random.randint(0, 255) b = random.randint(0, 255) return f"#{r:02x}{g:02x}{b:02x}"

def random_rgb(): """Generate random color as (R, G, B) tuple""" return (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))

def random_pastel(): """Generate soft pastel colors""" r = random.randint(150, 255) g = random.randint(150, 255) b = random.randint(150, 255) return f"#{r:02x}{g:02x}{b:02x}"

print("Random hex colors:") for _ in range(5): print(f" {random_hex_color()}")

print("\nRandom RGB tuples:") for _ in range(5): print(f" {random_rgb()}")

print("\nRandom pastels:") for _ in range(5): print(f" {random_pastel()}")

Explanation

Run the file:

Random hex colors:
  #3a7bc4
  #f2941d
  #8c2e5f
  ...

Random RGB tuples: (142, 87, 203) (56, 189, 34) ...

Random pastels: #d4f0e8 #f7c9db ...

Techniques:

  • randint(0, 255) covers the full color range
  • :02x formats as 2-digit hex
  • Constraining range (150-255) gives pastels

Why this matters

Data visualization often needs distinct random colors. Game development uses random palettes.

✓ Checkpoint

⚠ If something breaks here

Nothing should break. Experiment with different ranges.

What this page does

Uses random sampling to estimate the value of π.

Where this fits

Monte Carlo methods use randomness to solve mathematical problems.

Code (this page)

import random

def estimate_pi(num_points): """Estimate pi by throwing darts at a square.""" inside_circle = 0 for _ in range(num_points): x = random.uniform(-1, 1) y = random.uniform(-1, 1) # Check if point is inside unit circle if x*x + y*y <= 1: inside_circle += 1 # Area of circle / Area of square = pi/4 # So pi = 4 * (points in circle / total points) return 4 * inside_circle / num_points

print("Estimating π with random points:\n") for n in [100, 1000, 10000, 100000, 1000000]: estimate = estimate_pi(n) error = abs(estimate - 3.14159265359) print(f" {n:>8} points: π ≈ {estimate:.6f} (error: {error:.6f})")

Explanation

Run the file:

Estimating π with random points:

100 points: π ≈ 3.120000 (error: 0.021593) 1000 points: π ≈ 3.148000 (error: 0.006407) 10000 points: π ≈ 3.141200 (error: 0.000393) 100000 points: π ≈ 3.142120 (error: 0.000527) 1000000 points: π ≈ 3.141752 (error: 0.000159)

More points = better estimate. This demonstrates:

  • Random sampling can solve geometric problems
  • Law of large numbers: more samples → converges to true value

Why this matters

Monte Carlo methods power financial modeling, physics simulations, and AI game playing.

✓ Checkpoint

⚠ If something breaks here

Nothing should break. Large num_points takes longer to run.

What this page does

Shows how to use seeds for testing random code.

Where this fits

Testing code that uses randomness requires reproducibility.

Code (this page)

import random

def get_random_user(): """Generate a random user for testing.""" names = ["Alice", "Bob", "Charlie", "Diana", "Eve"] ages = range(18, 80) return { "name": random.choice(names), "age": random.choice(ages), "score": random.randint(0, 100) }

# Without seed: different each time print("Without seed (different each run):") for _ in range(3): print(f" {get_random_user()}")

# With seed: reproducible print("\nWith seed(42) (same each run):") random.seed(42) for _ in range(3): print(f" {get_random_user()}")

# Reset and get same results print("\nWith seed(42) again (identical):") random.seed(42) for _ in range(3): print(f" {get_random_user()}")

Explanation

Run the file multiple times:

Without seed (different each run):
  {'name': 'Charlie', 'age': 45, 'score': 72}
  ...

With seed(42) (same each run): {'name': 'Charlie', 'age': 35, 'score': 82} {'name': 'Diana', 'age': 67, 'score': 15} {'name': 'Bob', 'age': 28, 'score': 95}

With seed(42) again (identical): {'name': 'Charlie', 'age': 35, 'score': 82} {'name': 'Diana', 'age': 67, 'score': 15} {'name': 'Bob', 'age': 28, 'score': 95}

The seed(42) sections are always identical, even across runs.

Why this matters

Unit tests need deterministic results. "Test failed with seed 42" is actionable.

✓ Checkpoint

⚠ If something breaks here

Nothing should break. Seed values can be any integer.

What this page does

Summarizes all random module functions covered.

Where this fits

Quick reference after completing the guide.

Explanation

### Number Generation

FunctionReturnsRange
random()float[0.0, 1.0)
randint(a, b)int[a, b] inclusive
randrange(start, stop, step)int[start, stop) exclusive
uniform(a, b)float[a, b]
gauss(mu, sigma)floatnormal distribution

### Selection

FunctionReturnsReplacement
choice(seq)single itemN/A
choices(seq, k=n)list of n itemsWITH replacement
sample(seq, k=n)list of n itemsWITHOUT replacement

### Modification

FunctionEffect
shuffle(list)Randomizes list IN PLACE
seed(value)Sets random state for reproducibility

Why this matters

Reference tables accelerate lookup when you know what you need but forget the exact function.

✓ Checkpoint

⚠ If something breaks here

Nothing to break. This is reference material.

What this page does

Shows reusable code patterns for common tasks.

Where this fits

Copy-paste solutions for frequent needs.

Code (this page)

import random

# Pattern 1: Random item from list items = ["apple", "banana", "cherry"] item = random.choice(items)

# Pattern 2: Random integer in range (inclusive) dice = random.randint(1, 6)

# Pattern 3: Random float in range temp = random.uniform(60.0, 80.0)

# Pattern 4: Pick N unique items winners = random.sample(range(1, 100), k=5)

# Pattern 5: Pick N items (may repeat) flips = random.choices(["H", "T"], k=10)

# Pattern 6: Shuffle a list (modifies in place) deck = list(range(52)) random.shuffle(deck)

# Pattern 7: Shuffle without modifying original = [1, 2, 3, 4, 5] shuffled = random.sample(original, k=len(original))

# Pattern 8: Weighted selection prizes = random.choices( ["win", "lose"], weights=[10, 90], k=1 )[0]

# Pattern 9: Reproducible results random.seed(42) result = random.randint(1, 100) # Always 82

# Pattern 10: Random string import string password = ''.join(random.choices( string.ascii_letters + string.digits, k=12 ))

print("All patterns demonstrated.")

Explanation

These 10 patterns cover most random module use cases. Each is a self-contained solution you can adapt.

Why this matters

Having ready patterns speeds up development. You don't need to rediscover solutions.

✓ Checkpoint

⚠ If something breaks here

Nothing should break. These are proven patterns.

What this page does

Confirms you have mastered all skills in this guide.

Where this fits

This is the end. Verify everything works together.

Explanation

Complete this final test in your random_basics.py:

import random
import string

# Set seed for reproducibility random.seed(2026)

# 1. Generate 5 random integers 1-100 print("1. Random integers:") for _ in range(5): print(f" {random.randint(1, 100)}")

# 2. Pick 3 unique items from a list colors = ["red", "orange", "yellow", "green", "blue", "purple"] picked = random.sample(colors, k=3) print(f"\n2. Picked colors: {picked}")

# 3. Simulate weighted dice (6 is twice as likely) weights = [1, 1, 1, 1, 1, 2] # 6 has weight 2 rolls = random.choices([1, 2, 3, 4, 5, 6], weights=weights, k=100) print(f"\n3. In 100 weighted rolls, got 6 exactly {rolls.count(6)} times")

# 4. Shuffle a deck and deal 5 cards deck = [f"{r}{s}" for s in "♠♥♦♣" for r in "A23456789TJQK"] random.shuffle(deck) hand = [deck.pop() for _ in range(5)] print(f"\n4. Dealt hand: {hand}")

# 5. Generate a random password chars = string.ascii_letters + string.digits password = ''.join(random.choices(chars, k=16)) print(f"\n5. Password: {password}")

print("\n✓ Random Module Fundamentals Complete!")

Run it. If you understand every line, you've completed the guide.

Why this matters

You now have complete control over randomness in Python. From games to simulations to security, you can generate the random data you need.

✓ Checkpoint

Contents