有效地生成唯一的整数对
Posted
技术标签:
【中文标题】有效地生成唯一的整数对【英文标题】:Efficiently generating unique pairs of integers 【发布时间】:2013-03-25 11:06:25 【问题描述】:在 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
时会陷入循环。
有没有一种更简单、更简洁的方法来生成唯一的不同整数对?
【问题讨论】:
嗯,你有没有试过用unique(round(rand(n+20,2)*m),'rows')
生成一个A,测试长度是否至少为n
(如果不是,则重复该过程),然后选择第一个@ 987654338@ 行?这可能会慢一些,但值得一试
@jucestain 是的,我确实考虑过这一点——我在倒数第二段中提到了这种方法。我担心的是,如果 n 非常大,算法将不得不重复循环,直到它有效地偶然找到每一对。
【参考方案1】:
如果以正确的方式查看,简单、轻松。
您希望生成 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 x 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。一个不错的功能是不需要拒绝重复对。
【讨论】:
非常聪明,做得很好。将随机整数确定性地映射到成对的数字是一个巧妙的技巧。谢谢。 哇!我从来不知道 MATLAB 有这个功能。 +1! 重读一下,我才注意到原来的问题陈述中 [3,3] 会被拒绝。所以我会修改东西。变化很小。【参考方案2】:这更像是一种通用方法,而不是 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 是矩阵的高度。
【讨论】:
谢谢,这听起来很有希望,但我不太明白这个符号。x
是向量还是二维矩阵? n
和 y
是什么?谢谢。
所以我来自 C++ 背景。 x[n] 是 n x 1 矩阵,x[n][y] 是 n x y 矩阵,向量是 n x 1 矩阵。
好的,谢谢。我认为,当n
接近m(m-1)/2
时,确保尚未选择每一对的步骤可能会导致速度变慢,如上所述。
实际上它在 O(n) 中运行,因此您只需检查您上方的每个条目一次。因此,对于每个条目,您只需检查 x[n+1] 您不需要检查完整列表。
好的,我现在明白了,谢谢。我认为这会奏效,但我最终希望以相同的概率选择每一对可能的配对;我想这不会这样做?【参考方案3】:
以下代码可以满足您的需要:
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
【讨论】:
谢谢,这在n << m(m-1)/2
时效果很好,但是当我设置n = m(m-1)/2 - 1
时,这似乎永远运行,而我原来的方法只需要几秒钟。也许我需要两种不同的方法,具体取决于 n
和 m
的值...
@BillCheatham:也许是这样。 n
的值越小,m
的值越大,效果越好。
如果我理解正确,这将永远持续下去,因为您只是使用 rand(n,2)
而不是像我在 cmets 中建议的 rand(n+padding,2)
。填充将减少必须再次重新计算 my_list
的可能性。之后只需选择 my_list 的前 n 行...这是一种简单的方法,但可能有一种在数学上更合理、更直接的方法来实现它。以上是关于有效地生成唯一的整数对的主要内容,如果未能解决你的问题,请参考以下文章