Algorithm Analysis and Implementation for Efficient Generation of Non-Repeating Random Numbers

Nov 21, 2025 · Programming · 10 views · 7.8

Keywords: Non-repeating Random Numbers | Java Algorithms | Performance Optimization

Abstract: This paper provides an in-depth exploration of multiple methods for generating non-repeating random numbers in Java, focusing on the Collections.shuffle algorithm, LinkedHashSet collection algorithm, and range adjustment algorithm. Through detailed code examples and complexity analysis, it helps developers choose optimal solutions based on specific requirements while avoiding common performance pitfalls and implementation errors.

Problem Background and Challenges

In programming practice, generating sequences of non-repeating random numbers is a common requirement. When extracting a specified number of unique random numbers from a limited range, the simple approach of generating numbers one by one and checking for duplicates may be acceptable for small ranges. However, as the range expands, this method faces significant performance issues. For example, when MAX is 20, using brute-force checking leads to rapidly increasing code complexity and poor efficiency.

Collections.shuffle Algorithm

For scenarios where the range size equals the number of elements needed, Collections.shuffle offers an elegant solution. This algorithm first creates a list containing all possible numbers, then ensures uniqueness through random shuffling.

import java.util.*;

public class ShuffleExample {
    public static List<Integer> generateUniqueRandom(int max) {
        List<Integer> numbers = new ArrayList<>();
        for (int i = 1; i <= max; i++) {
            numbers.add(i);
        }
        Collections.shuffle(numbers);
        return numbers;
    }
}

The advantage of this method lies in its O(n) time complexity and concise implementation. It is particularly suitable for shuffling applications, such as dealing cards in poker games. However, when the number of required elements is much smaller than the range size, this approach wastes significant memory and computational resources.

LinkedHashSet Collection Algorithm

When extracting a small number of unique random numbers from a large range, set-based algorithms are more efficient. LinkedHashSet combines the fast lookup of hash tables with the order preservation of linked lists, making it ideal for such scenarios.

import java.util.*;

public class SetBasedGenerator {
    public static Set<Integer> generateUniqueRandom(int max, int numbersNeeded) {
        if (max < numbersNeeded) {
            throw new IllegalArgumentException("Cannot request more numbers than available");
        }
        
        Random rng = new Random();
        Set<Integer> generated = new LinkedHashSet<>();
        
        while (generated.size() < numbersNeeded) {
            Integer next = rng.nextInt(max) + 1;
            generated.add(next);
        }
        
        return generated;
    }
}

The time complexity of this algorithm depends on collision probability, with O(k) in the best case, where k is the number of required elements. The choice of LinkedHashSet is crucial as it ensures the insertion order of the result sequence, which is significant in many application scenarios.

Range Adjustment Algorithm

The third method ensures each generation produces valid numbers by dynamically adjusting the generation range, thus avoiding duplicate checks. This approach is more memory-efficient and particularly suitable for resource-constrained environments.

import java.util.*;

public class RangeAdjustmentGenerator {
    public static List<Integer> generateUniqueRandom(int max, int count) {
        if (count > max) {
            throw new IllegalArgumentException("Count exceeds range");
        }
        
        Random rng = new Random();
        List<Integer> available = new ArrayList<>();
        List<Integer> result = new ArrayList<>();
        
        // Initialize available numbers list
        for (int i = 1; i <= max; i++) {
            available.add(i);
        }
        
        for (int i = 0; i < count; i++) {
            int index = rng.nextInt(available.size());
            result.add(available.get(index));
            available.remove(index);
        }
        
        return result;
    }
}

This algorithm has O(k²) time complexity and performs well when k is small. Each removal operation requires O(n) time, so performance degrades when k approaches n.

Performance Analysis and Selection Guidelines

In practical applications, algorithm selection should be based on specific requirements:

Developers should consider factors such as data scale, performance requirements, and memory constraints to choose the most appropriate algorithm implementation.

Implementation Considerations

Several key points require attention during implementation:

  1. Random number generator instantiation should follow best practices, avoiding repeated creation in loops
  2. Boundary condition handling, including parameter validation and exception management
  3. For concurrent environments, thread safety considerations are necessary
  4. In large-scale applications, algorithm scalability and maintainability should be considered

By properly selecting algorithms and paying attention to implementation details, efficient and reliable solutions for generating non-repeating random numbers can be constructed.

Copyright Notice: All rights in this article are reserved by the operators of DevGex. Reasonable sharing and citation are welcome; any reproduction, excerpting, or re-publication without prior permission is prohibited.