在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
时卡在循环中。
是否有更简单,更清晰的方法来生成唯一的不同整数对?
答案 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