Random number generator that returns only one number each time

Viewed 5256

Does Python have a random number generator that returns only one random integer number each time when next() function is called? Numbers should not repeat and the generator should return random integers in the interval [1, 1 000 000] that are unique.

I need to generate more than million different numbers and that sounds as if it is very memory consuming in case all the number are generated at same time and stored in a list.

7 Answers

I just needed that function, and to my huge surprise I haven't found anything that would suit my needs. @poke's answer didn't satisfy me because I needed to have precise borders, and other ones which included lists caused heaped memory.

Initially, I needed a function that would generate numbers from a to b, where a - b could be anything from 0 to 2^32 - 1, which means the range of those numbers could be as high as maximal 32-bit unsigned integer.

The idea of my own algorithm is simple both to understand and implement. It's a binary tree, where the next branch is chosen by 50/50 chance boolean generator. Basically, we divide all numbers from a to b into two branches, then decide from which one we yield the next value, then do that recursively until we end up with single nodes, which are also being picked up by random.

The depth of recursion is:

\log+_{2}+(b - a)

, which implies that for the given stack limit of 256, your highest range would be 2^256, which is impressive.

Things to note:

  1. a must be lesser or equal b - otherwise no output will be displayed.
  2. Boundaries are included, meaning unique_random_generator(0, 3) will generate [0, 1, 2, 3].

TL;DR - here's the code

import math, random

# a, b - inclusive
def unique_random_generator(a, b):
    
    # corner case on wrong input
    if a > b:
        return

    # end node of the tree
    if a == b:
        yield a
        return
    
    # middle point of tree division
    c = math.floor((a + b) / 2)
    
    generator_left = unique_random_generator(a, c) # left branch - contains all the numbers between 'a' and 'c'
    generator_right = unique_random_generator(c + 1, b) # right branch - contains all the numbers between 'c + 1' and 'b'

    has_values = True
    while (has_values):
        # decide whether we pick up a value from the left branch, or the right
        decision = bool(random.getrandbits(1))

        if decision:
            next_left = next(generator_left, None)
            
            # if left branch is empty, check the right one
            if next_left == None:
                next_right = next(generator_right, None)
                
                # if both empty, current recursion's dessicated
                if next_right == None:
                    has_values = False
                else:
                    yield next_right
            else:
                yield next_left
                next_right = next(generator_right, None)
                
                if next_right != None:
                    yield next_right
        else:
            next_right = next(generator_right, None)
            
            # if right branch is empty, check the left one
            if next_right == None:
                next_left = next(generator_left, None)
                
                # if both empty, current recursion's dessicated
                if next_left == None:
                    has_values = False
                else:
                    yield next_left
            else:
                yield next_right
                next_left = next(generator_left, None)
                
                if next_left != None:
                    yield next_left

Usage:

for i in unique_random_generator(0, 2**32):
    print(i)
Related