Generate 3 distinct random integers via a uniformly distributed RNG

我是研究僧i 提交于 2021-01-28 18:01:25

问题


Suppose I have a randint(A, B) function which generates a uniformly distributed random integer in the (inclusive) range [A, B]. For example, 1 <= randint(1, 10) <= 10. How can I efficiently randomly generate 3 distinct integers within a certain range?

This should be done with exactly 3 calls to randint(A, B). I don't care which permutation of the three numbers I get; it can be random or have a defined order.

I'm not just trying to find any algorithm to do this, but (within reason) the fastest ones which use the least additional memory.

def rand3(low, high):
    assert high - low + 1 >= 3
    # Magic!
    return n1, n2, n3 # each number has low <= n <= high, each number is distinct

The easiest way to do this, albeit very expensive, is to generate a list of numbers [low, low + 1, ..., high], then do the Fisher-Yates shuffle to select 3 elements. It's not necessary to fully evaluate Fischer-Yates, as we can just roll three indices. However, I definitely do not want to create this array in memory.


For 2 integers, it suffices to roll the second integer in the range less one, adjusting its value upward if it's greater than or equal to the first number:

def rand2(low, high):
    i1 = randint(low, high)
    i2 = randint(low, high - 1)
    n1 = i1
    n2 = i2 + (i2 >= n1)
    return n1, n2

However, it's not straightforward to extend this to three integers. I believe it should be possible to do something like this:

# I _think_ this works?
def rand3(low, high):
    i1 = randint(low, high)
    i2 = randint(low, high - 1)
    i3 = randint(low, high - 2)

    n1 = i1
    n2 = i2 + (i2 >= n1)
    if n1 > n2: n1, n2 = n2, n1
    n3 = i3 + (i3 >= n1)
    n3 = n3 + (n3 >= n2)

    return n1, n2, n3

However, I feel like there has to be something simpler than this. Is the 3 element problem really that much more complex that we need to sort the 2 integers and that the second conditional (n3 >= n2) must depend on an intermediate value of n3? I was expecting something much closer to the 2 element problem.


回答1:


I don't know if these variations make you happier, but this rand3 has one fewer comparison at least.

The idea is to simulate a couple steps of Fisher--Yates. In rand3, we work backward from the index i3 to find the value. The first if undoes the second swap, and the second if undoes the first swap.

import collections
from random import randint


def rand2(low, high):
    i1 = randint(low, high)
    i2 = randint(low, high - 1)
    if i2 == i1:
        i2 = high
    return i1, i2


def rand3(low, high):
    i1 = randint(low, high)
    i2 = randint(low, high - 1)
    i3 = randint(low, high - 2)
    if i3 == i2:
        i3 = high - 1
    if i3 == i1:
        i3 = high
    if i2 == i1:
        i2 = high
    return i1, i2, i3


print(collections.Counter(rand3(1, 4) for i in range(1000000)))



回答2:


The easiest, though probably not most performant way is to just keep rechoosing the random numbers when they don't meet the criteria. Checking for duplicates is trivial, just make a set and the duplicates will be thrown out.

def rand3(low, high):
    assert high - low + 1 >= 3
    s = set()
    while len(s) != 3:
        s = set([randint(low, high), randint(low, high), randint(low, high)])
    return tuple(set)


来源:https://stackoverflow.com/questions/64358470/generate-3-distinct-random-integers-via-a-uniformly-distributed-rng

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!