Python Get Random Unique N Pairs

时间:2019-03-19 15:05:29

标签: python algorithm math graph combinations

Say I have a range(1, n + 1). I want to get m unique pairs.

What I found is, if the number of pairs is close to n(n-1)/2 (maxiumum number of pairs), one can't simply generate random pairs everytime because they will start overriding eachother. I'm looking for a somewhat lazy solution, that will be very efficient (in Python's world).

My attempt so far:

def get_input(n, m):

    res = str(n) + "\n" + str(m) + "\n"
    buffet = range(1, n + 1)
    points = set()

    while len(points) < m:
        x, y = random.sample(buffet, 2)
        points.add((x, y)) if x > y else points.add((y, x)) # meeh

    for (x, y) in points:
        res += "%d %d\n" % (x, y);

    return res

3 个答案:

答案 0 :(得分:3)

You can use combinations to generate all pairs and use sample to choose randomly. Admittedly only lazy in the "not much to type" sense, and not in the use a generator not a list sense :-)

from itertools import combinations
from random import sample

n = 100
sample(list(combinations(range(1,n),2)),5)

If you want to improve performance you can make it lazy by studying this Python random sample with a generator / iterable / iterator

the generator you want to sample from is this: combinations(range(1,n)

答案 1 :(得分:2)

这是一种方法,它采用0 to n*(n-1)/2 - 1范围内的数字并将其解码为0 to n-1范围内的一对唯一项。为了方便起见,我使用了基于0的数学,但是如果愿意,您当然可以将1添加到所有返回的对中:

import math
import random

def decode(i):
    k = math.floor((1+math.sqrt(1+8*i))/2)
    return k,i-k*(k-1)//2

def rand_pair(n):
    return decode(random.randrange(n*(n-1)//2))

def rand_pairs(n,m):
    return [decode(i) for i in random.sample(range(n*(n-1)//2),m)]

例如:

>>> >>> rand_pairs(5,8)
[(2, 1), (3, 1), (4, 2), (2, 0), (3, 2), (4, 1), (1, 0), (4, 0)]

数学很难轻易解释,但是k定义中的decode是通过求解二次方程获得的,该二次方程给出triangular numbers的数量为{{1} },以及<= i处在三角形数字序列中的位置,告诉您如何从中解码出唯一的一对。关于此解码的有趣之处在于,它根本不使用i,而是实现了从一组自然数(从0开始)到所有自然对对的一对一的对应关系。

答案 2 :(得分:1)

I don't think any thing on your line can improve. After all, as your m get closer and closer to the limit n(n-1)/2, you have thinner and thinner chance to find the unseen pair.

I would suggest to split into two cases: if m is small, use your random approach. But if m is large enough, try

 pairs = list(itertools.combination(buffet,2))
 ponits = random.sample(pairs, m)

Now you have to determine the threshold of m that determines which code path it should go. You need some math here to find the right trade off.