有效地生成唯一的整数对

时间:2013-04-03 16:40:21

标签: matlab random 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

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

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

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

3 个答案:

答案 0 :(得分: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);

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

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

{[1,2], [1,3], [2,3], [1,4], [2,4], [3,4], ..., [m-1,m]}

我们可以把它们看作是一个严格的上三角形数组中的元素,大小为m乘m,因此那些元素位于主对角线之上。

q = floor(sqrt(8*(k-1) + 1)/2 + 1/2);
p = k - q.*(q-1)/2;

看到这些公式从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

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

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
是的,看起来一切都很完美。现在尝试使用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.

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

答案 1 :(得分:1)

这更像是一种通用方法,而不是matlab解决方案。

如果您按照以下方式填充矢量,请执行以下操作。

x[n] = rand()
x[n + 1] = x[n] + rand() %% where rand can be equal to 0.

然后再次执行以下操作

x[n][y] = x[n][y] + rand() + 1

如果

x[n] == x[n+1]

您将确保尚未选择同一对。

完成后,如果希望它们随机分布,可以在矩阵上运行置换算法。

这种方法将为您提供所有可能性或2个整数对,并且它在O(n)中运行,其中n是矩阵的高度。

答案 2 :(得分:1)

以下代码可满足您的需求:

n = 10000;
m = 500;
my_list = unique(sort(round(rand(n,2)*m),2),'rows');
my_list = my_list(find((my_list(:,1)==my_list(:,2))==0),:);
%temp = my_list;    %In case you want to check what you initially generated.
while(size(my_list,1)~=n)
    %my_list = unique([my_list;sort(round(rand(1,2)*m),2)],'rows');
    %Changed as per @jucestain's suggestion.
    my_list = unique([my_list;sort(round(rand((n-size(my_list,1)),2)*m),2)],'rows');
    my_list = my_list(find((my_list(:,1)==my_list(:,2))==0),:);
end