xbe.at

Python / Python for ML and AI

Generating Unique Random Numbers in Python

7 min read, 15 sections

Contents
  1. Basic Random Number Generation Without Duplicates Using Lists
  2. Set-Based Random Number Generation
  3. Cryptographic Random Number Generation
  4. Fisher-Yates Shuffle Implementation
  5. Generator-Based Random Number Implementation
  6. Weighted Random Number Generation
  7. Time-Based Seeding for Reproducibility
  8. Binary Search Tree-Based Generation
  9. Prime Number Random Generation
  10. Real-World Application: Monte Carlo Simulation
  11. Sliding Window Random Generation
  12. Permutation-Based Random Generation
  13. Real-World Application: Random Sampling for Cross-Validation
  14. Sequence Pattern Generation
  15. Additional Resources

Basic Random Number Generation Without Duplicates Using Lists

Understanding how to generate unique random numbers is crucial for various applications, from sampling to simulation. The simplest approach uses Python's random module with a list comprehension to create a pool of numbers and randomly select from it.

import random

def generate_unique_numbers(start, end, count):
    # Create a pool of numbers and randomly sample from it
    number_pool = list(range(start, end + 1))
    unique_numbers = random.sample(number_pool, count)
    return sorted(unique_numbers)

# Example usage
result = generate_unique_numbers(1, 100, 10)
print(f"10 unique random numbers between 1 and 100: {result}")

Set-Based Random Number Generation

Sets provide an alternative approach to generating unique random numbers, leveraging their inherent property of storing unique elements. This method is particularly efficient when dealing with large ranges and preventing duplicates.

import random

def generate_unique_with_set(start, end, count):
    unique_numbers = set()
    while len(unique_numbers) < count:
        unique_numbers.add(random.randint(start, end))
    return sorted(list(unique_numbers))

# Example usage
result = generate_unique_with_set(1, 50, 10)
print(f"10 unique random numbers between 1 and 50: {result}")

Cryptographic Random Number Generation

For applications requiring cryptographic security, we use the secrets module to generate unique random numbers. This approach is essential for security-critical applications like token generation or encryption keys.

import secrets
import random

def crypto_unique_numbers(start, end, count):
    number_pool = list(range(start, end + 1))
    return sorted(secrets.SystemRandom().sample(number_pool, count))

# Example usage
result = crypto_unique_numbers(1, 100, 5)
print(f"5 cryptographically secure unique numbers: {result}")

Fisher-Yates Shuffle Implementation

The Fisher-Yates shuffle algorithm provides an efficient method for generating unique random numbers through array shuffling. This implementation demonstrates the algorithm's core concepts and its application.

def fisher_yates_selection(start, end, count):
    # Create array of numbers
    arr = list(range(start, end + 1))
    n = len(arr)
    
    # Perform Fisher-Yates shuffle for 'count' elements
    for i in range(count):
        j = random.randint(i, n-1)
        arr[i], arr[j] = arr[j], arr[i]
    
    return sorted(arr[:count])

# Example usage
result = fisher_yates_selection(1, 30, 8)
print(f"8 unique random numbers using Fisher-Yates: {result}")

Generator-Based Random Number Implementation

Using Python generators provides a memory-efficient way to generate unique random numbers, especially useful when dealing with large ranges or when numbers need to be generated lazily.

def unique_number_generator(start, end, count):
    seen = set()
    while len(seen) < count:
        num = random.randint(start, end)
        if num not in seen:
            seen.add(num)
            yield num

# Example usage
generator = unique_number_generator(1, 20, 5)
result = sorted(list(generator))
print(f"5 unique random numbers using generator: {result}")

Weighted Random Number Generation

In real-world applications, we often need to generate unique random numbers with specific weights or probabilities. This implementation demonstrates how to achieve weighted random selection while maintaining uniqueness.

import numpy as np

def weighted_unique_random(start, end, count, weights=None):
    numbers = list(range(start, end + 1))
    if weights is None:
        weights = [1] * len(numbers)
    
    selected = []
    weights = np.array(weights)
    
    for _ in range(count):
        # Normalize weights
        weights_norm = weights / weights.sum()
        # Select number
        chosen_idx = np.random.choice(len(numbers), p=weights_norm)
        selected.append(numbers[chosen_idx])
        # Remove selected number and its weight
        numbers.pop(chosen_idx)
        weights = np.delete(weights, chosen_idx)
    
    return sorted(selected)

# Example usage
weights = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]
result = weighted_unique_random(1, 10, 5, weights)
print(f"5 weighted unique random numbers: {result}")

Time-Based Seeding for Reproducibility

Implementing reproducible random number generation is crucial for testing and validation. This approach uses time-based seeding to create reproducible sequences of unique random numbers.

import time

def time_seeded_unique_random(start, end, count, seed=None):
    if seed is None:
        seed = int(time.time())
    
    random.seed(seed)
    numbers = random.sample(range(start, end + 1), count)
    
    return {
        'numbers': sorted(numbers),
        'seed': seed
    }

# Example usage
result1 = time_seeded_unique_random(1, 100, 5, seed=42)
result2 = time_seeded_unique_random(1, 100, 5, seed=42)
print(f"First generation: {result1}")
print(f"Second generation (same seed): {result2}")

Binary Search Tree-Based Generation

A binary search tree implementation provides an interesting approach to generating unique random numbers while maintaining ordering properties and ensuring uniqueness through tree structure.

class Node:
    def __init__(self, value):
        self.value = value
        self.left = None
        self.right = None

class BSTRandomGenerator:
    def __init__(self):
        self.root = None
    
    def insert(self, value):
        if not self.root:
            self.root = Node(value)
            return True
        
        current = self.root
        while True:
            if value < current.value:
                if not current.left:
                    current.left = Node(value)
                    return True
                current = current.left
            elif value > current.value:
                if not current.right:
                    current.right = Node(value)
                    return True
                current = current.right
            else:
                return False

    def generate_unique_numbers(self, start, end, count):
        numbers = []
        while len(numbers) < count:
            num = random.randint(start, end)
            if self.insert(num):
                numbers.append(num)
        return sorted(numbers)

# Example usage
bst_generator = BSTRandomGenerator()
result = bst_generator.generate_unique_numbers(1, 50, 10)
print(f"10 unique numbers using BST: {result}")

Prime Number Random Generation

Generating unique random prime numbers presents a specialized case of unique random number generation, useful in cryptography and number theory applications.

def is_prime(n):
    if n < 2:
        return False
    for i in range(2, int(n ** 0.5) + 1):
        if n % i == 0:
            return False
    return True

def generate_unique_primes(start, end, count):
    primes = [num for num in range(start, end + 1) if is_prime(num)]
    if len(primes) < count:
        raise ValueError("Not enough primes in the given range")
    return sorted(random.sample(primes, count))

# Example usage
try:
    result = generate_unique_primes(1, 100, 5)
    print(f"5 unique random prime numbers: {result}")
except ValueError as e:
    print(f"Error: {e}")

Real-World Application: Monte Carlo Simulation

This implementation demonstrates how unique random number generation can be applied to Monte Carlo simulation for estimating π using random point generation in a square.

import math

def monte_carlo_pi(points):
    inside_circle = 0
    unique_points = set()
    
    while len(unique_points) < points:
        x = random.uniform(-1, 1)
        y = random.uniform(-1, 1)
        point = (round(x, 6), round(y, 6))
        
        if point not in unique_points:
            unique_points.add(point)
            if math.sqrt(x*x + y*y) <= 1:
                inside_circle += 1
    
    pi_estimate = 4 * inside_circle / points
    return pi_estimate, unique_points

# Example usage
points = 1000
estimated_pi, unique_points = monte_carlo_pi(points)
print(f"π estimate using {points} unique points: {estimated_pi}")
print(f"Actual π: {math.pi}")
print(f"Error: {abs(estimated_pi - math.pi)}")

Sliding Window Random Generation

This advanced technique implements a sliding window approach to generate unique random numbers within a moving range, useful for time-series analysis and streaming data applications.

class SlidingWindowRandomGenerator:
    def __init__(self, window_size):
        self.window_size = window_size
        self.history = set()
        
    def generate(self, start, count):
        window_start = start
        window_end = start + self.window_size
        result = []
        
        while len(result) < count:
            # Generate candidate within current window
            candidate = random.randint(window_start, window_end)
            
            # Check if number is unique in history
            if candidate not in self.history:
                self.history.add(candidate)
                result.append(candidate)
                
                # Slide window if needed
                if len(result) % (self.window_size // 2) == 0:
                    window_start += self.window_size // 2
                    window_end += self.window_size // 2
                    
        return sorted(result)

# Example usage
generator = SlidingWindowRandomGenerator(window_size=20)
result = generator.generate(start=1, count=10)
print(f"10 unique numbers using sliding window: {result}")

Permutation-Based Random Generation

A permutation-based approach that generates unique random numbers by creating controlled permutations of existing sequences, particularly useful for maintaining specific distribution properties.

def permutation_based_random(start, end, count):
    # Create base sequence
    base_sequence = list(range(start, end + 1))
    
    # Generate permutation parameters
    permutation_params = {
        'shift': random.randint(1, len(base_sequence)),
        'multiply': random.choice([num for num in range(1, len(base_sequence)) 
                                 if math.gcd(num, len(base_sequence)) == 1])
    }
    
    # Apply permutation
    result = []
    used = set()
    current = start
    
    while len(result) < count:
        # Generate next number using permutation formula
        next_num = ((current * permutation_params['multiply'] + 
                     permutation_params['shift']) % (end - start + 1)) + start
        
        if next_num not in used:
            used.add(next_num)
            result.append(next_num)
        current = next_num
    
    return sorted(result)

# Example usage
result = permutation_based_random(1, 50, 15)
print(f"15 unique numbers using permutation: {result}")

Real-World Application: Random Sampling for Cross-Validation

This implementation demonstrates how to use unique random number generation for creating cross-validation folds in machine learning applications.

import numpy as np

def create_cv_folds(dataset_size, n_folds):
    # Generate unique indices for the entire dataset
    all_indices = list(range(dataset_size))
    fold_size = dataset_size // n_folds
    
    # Create folds using unique random sampling
    folds = []
    remaining_indices = set(all_indices)
    
    for fold in range(n_folds - 1):
        # Sample indices for current fold
        fold_indices = set(random.sample(list(remaining_indices), fold_size))
        folds.append(sorted(list(fold_indices)))
        remaining_indices -= fold_indices
    
    # Add remaining indices to last fold
    folds.append(sorted(list(remaining_indices)))
    
    return folds

# Example usage with synthetic dataset
dataset_size = 100
n_folds = 5

cv_folds = create_cv_folds(dataset_size, n_folds)
for i, fold in enumerate(cv_folds):
    print(f"Fold {i+1} size: {len(fold)}")
    print(f"First 5 indices in fold {i+1}: {fold[:5]}")

Sequence Pattern Generation

Implementation of a pattern-based unique random number generator that creates sequences following specific mathematical patterns while maintaining randomness and uniqueness.

def pattern_based_generator(start, end, count, pattern_type='fibonacci'):
    def fibonacci_pattern(n):
        return int((1 + math.sqrt(5))**n / math.sqrt(5))
    
    def geometric_pattern(n):
        return int(2**n)
    
    patterns = {
        'fibonacci': fibonacci_pattern,
        'geometric': geometric_pattern
    }
    
    pattern_func = patterns.get(pattern_type, fibonacci_pattern)
    
    # Generate pattern-based numbers
    candidates = set()
    n = 0
    while len(candidates) < count * 2:  # Generate extra numbers for flexibility
        num = pattern_func(n) % (end - start + 1) + start
        if start <= num <= end:
            candidates.add(num)
        n += 1
    
    # Randomly select required number of values
    return sorted(random.sample(list(candidates), count))

# Example usage
fib_result = pattern_based_generator(1, 100, 8, 'fibonacci')
geo_result = pattern_based_generator(1, 100, 8, 'geometric')
print(f"Fibonacci pattern: {fib_result}")
print(f"Geometric pattern: {geo_result}")

Additional Resources

  • "Random Number Generation Techniques in Machine Learning" - https://arxiv.org/abs/1803.10122
  • "Analysis of Random Number Generation Methods" - https://scholar.google.com/
  • "Cryptographic Random Number Generation: A Comprehensive Survey" - https://scholar.google.com/
  • Suggested search terms for Google Scholar:
    • "Unique random number generation algorithms"
    • "Random sampling without replacement methods"
    • "Efficient unique random number generation"

More in Python for ML and AI

  1. Genetic Algorithms for Image Blending in Python6 min

    Genetic algorithms are optimization techniques inspired by natural selection. In the context of image combination, they can be used to evolve and blend...

  2. Fractal Dimension Analysis of Images using Python9 min

    Fractal dimension is a measure of complexity that quantifies how a pattern's detail changes with scale. In image analysis, it can reveal intricate structures...

  3. Guide to Comparing Group Means Across Multiple Variables in Python7 min

    Comparing group means is a fundamental task in data analysis, allowing us to understand differences between various subsets of our data. This guide will walk...

  4. Exponentially Weighted Moving Averages in Deep Learning with Python10 min

    Exponentially Weighted Moving Averages (EWMA) are a powerful technique used in deep learning for various purposes, including optimization, parameter updates...

  5. Guide to Self-Attention Network Architecture in Python7 min

    Self-attention networks are a fundamental component of modern deep learning architectures, particularly in natural language processing. They allow models to...

  6. Exploring the Wait-Time Paradox with Python6 min

    Have you ever wondered why the longer you wait on hold, the more time you expect to keep waiting? This phenomenon is known as the "wait-time paradox," and it...