Вот подход, который работает, беря число в диапазоне 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
получается путем решения квадратного уравнения, которое дает число треугольных чисел , которые равны <= i
, и где i
попадает впоследовательность треугольных чисел говорит вам, как декодировать уникальную пару из нее.Что интересно в этом декодере, так это то, что он вообще не использует n
, а реализует взаимно-однозначное соответствие от набора натуральных чисел (начиная с 0) до набора всех пар натуральных чисел.