有效地生成唯一的整数对

Bil*_*ham 14 random matlab integer

在MATLAB中,我想生成n范围内的随机整数对[1, m],其中每对都是唯一的.为了唯一性,我认为对中的数字的顺序是无关的,[3, 10]等于[10, 3].此外,每对应由两个不同的整数组成; 即[3, 4]好,但[3, 3]会被拒绝. 编辑:应该选择具有相同可能性的每个可能的对.

(显然,对参数的约束是n <= m(m-1)/2.)

m很小的时候就能成功地做到这一点,就像这样:

m = 500; n = 10;                   % setting parameters

A = ((1:m)'*ones(1, m));           % each column has the numbers 1 -> m
idxs1 = squareform(tril(A', -1))'; 
idxs2 = squareform(tril(A, -1))';   
all_pairs = [idxs1, idxs2];        % this contains all possible pairs

idx_to_use = randperm( size(all_pairs, 1), n );  % choosing random n pairs
pairs = all_pairs(idx_to_use, :)       

pairs =

   254   414
   247   334
   111   146
   207   297
    45   390
   229   411
     9    16
    75   395
    12   338
    25   442
Run Code Online (Sandbox Code Playgroud)

但是,矩阵A的大小m x m,意味着当m变大(例如超过10,000)时,MATLAB会耗尽内存.

我考虑过生成一堆随机数randi(m, [n, 2]),并反复拒绝重复的行,但我担心在n接近时陷入循环m(m-1)/2.

是否有更简单,更清晰的方法来生成独特的不同整数对?

小智 11

当以正确的方式观看时,容易,轻松.

你希望生成n对整数,[p,q],使得p和q位于区间[1,m]和p中

有多少可能的对?对的总数仅为m*(m-1)/ 2.(即1到m-1之间的数字之和.)

因此我们可以在[1,m*(m-1)/ 2]范围内生成n个随机整数.Randperm做得很好.(较旧的matlab版本不允许第二个参数为randperm.)

k = randperm(m/2*(m-1),n);
Run Code Online (Sandbox Code Playgroud)

(请注意,我用滑稽的方式用m表示这个表达式,在可能是一个奇怪的地方除以2.这避免了m值接近上限的精度问题.)

现在,如果我们将每个可能的对[p,q]与k中的一个整数相关联,我们可以向后工作,从k中生成的整数到一对[p,q].因此,该列表中的前几对是:

{[1,2], [1,3], [2,3], [1,4], [2,4], [3,4], ..., [m-1,m]}
Run Code Online (Sandbox Code Playgroud)

我们可以将它们视为大小为m乘m的严格上三角形阵列中的元素,因此这些元素位于主对角线上方.

q = floor(sqrt(8*(k-1) + 1)/2 + 1/2);
p = k - q.*(q-1)/2;
Run Code Online (Sandbox Code Playgroud)

看到这些公式从k中的展开元素中恢复p和q.我们可以说服自己这确实有效,但也许这里的一个简单方法就是这个测试:

k = 1:21;
q = floor(sqrt(8*(k-1) + 1)/2 + 3/2);
p = k - (q-1).*(q-2)/2;
[k;p;q]'

ans =
     1     1     2
     2     1     3
     3     2     3
     4     1     4
     5     2     4
     6     3     4
     7     1     5
     8     2     5
     9     3     5
    10     4     5
    11     1     6
    12     2     6
    13     3     6
    14     4     6
    15     5     6
    16     1     7
    17     2     7
    18     3     7
    19     4     7
    20     5     7
    21     6     7
Run Code Online (Sandbox Code Playgroud)

另一种测试方法是显示所有对都是针对小案例生成的.

m = 5;
n = 10;
k = randperm(m/2*(m-1),n);
q = floor(sqrt(8*(k-1) + 1)/2 + 3/2);
p = k - (q-1).*(q-2)/2;

sortrows([p;q]',[2 1])
ans =
     1     2
     1     3
     2     3
     1     4
     2     4
     3     4
     1     5
     2     5
     3     5
     4     5
Run Code Online (Sandbox Code Playgroud)

是的,看起来一切都很完美.现在尝试使用m和n的一些大数来测试使用的时间.

tic
m = 1e6;
n = 100000;
k = randperm(m/2*(m-1),n);
q = floor(sqrt(8*(k-1) + 1)/2 + 3/2);
p = k - (q-1).*(q-2)/2;
toc

Elapsed time is 0.014689 seconds.
Run Code Online (Sandbox Code Playgroud)

由于双精度的精度误差,该方案在失败之前将适用于大约1e8的m.在m/2*(m-1)超过2 ^ 53之前,精确限制应该是m不大于134217728.一个很好的功能是不需要拒绝重复对.