我们如何有效地从k
生成[1,...,N]
个随机和非连续样本?
(N=10, k=4)
的非期望示例:
2,3,8,10
这不是一个理想的例子,因为2
和3
是连续的。
(N=10, k=4)
的所需示例:
2,6,8,10
这是一个很好的例子,因为每对样本之间的差异大于1
答案 0 :(得分:15)
sort(randperm(N-(k-1),k))+[0:(k-1)]
此解决方案背后有一个简单的诅咒,如果您对问题采取任何已排序的解决方案并减去[0:(k-1)]
,您最终会随机选择N-(k-1)
中的k个数字
答案 1 :(得分:4)
让 S 表示所有k
- 元素向量的集合,其值取自[1,...,N]
,没有任何连续值。要通过 S 统一分布进行随机抽样,您可以使用拒绝方法:
在Matlab中,很容易生成均匀分布的k
- 元素向量,其值取自[1,...,N]
而无需替换(函数randsample
)。因此,这用作示例空间 T :
k = 4;
N = 10;
result = [1 1]; % // just to get while loop started
while any(diff(result)<=1) % // does not meet condition: try again
result = sort(randsample(N, k).'); %'// random sample without replacement
end
答案 2 :(得分:3)
正确检查每对样本的Python类。你有责任不传递一组不可能的数字(比如N = 10,k = 100)。
>>> class NonConsecutiveSampler(object):
def __init__(self,N):
import random
self.num = N
def get_samples(self,k):
possibilities = [i for i in range(1,self.num + 1)]
samples = []
while len(samples) < k:
r = random.sample(possibilities,1)[0]
samples.append(r)
for i in range(r - 1, r + 2):
if i in possibilities:
possibilities.remove(i)
samples.sort()
return samples
>>> n = NonConsecutiveSampler(10)
>>> n.get_samples(4)
[2, 5, 8, 10]
>>> n.get_samples(4)
[1, 5, 7, 10]
>>> n.get_samples(4)
[3, 6, 8, 10]
>>> n.get_samples(4)
[1, 3, 5, 8]
编辑:提高 效率
答案 3 :(得分:2)
您可以使样本之间的增量均匀分布在2和N-1之间(以避免连续和重复的数字):
from numpy import cumsum, floor, mod, random
N=5
k=100
increments = floor(random.rand(1,k)*(N-2))+2
out = mod(cumsum(increments), N)+1
print(out)
[ 5. 3. 1. 5. 2. 4. 3. 2. 4. 2. 4. 3. 1. 5. 4. 3. 5. 4.
2. 5. 4. 2. 5. 2. 4. 1. 5. 4. 1. 5. 3. 1. 3. 2. 4. 1.
5. 4. 1. 3. 5. 4. 3. 5. 2. 1. 3. 2. 4. 3. 1. 4. 2. 1.
3. 2. 1. 4. 3. 2. 1. 3. 5. 3. 5. 4. 2. 4. 2. 1. 3. 2.
1. 3. 5. 2. 5. 4. 3. 1. 4. 1. 4. 3. 5. 4. 2. 1. 5. 2.
1. 5. 4. 2. 4. 3. 5. 2. 4. 1.]
在python中相同:
{{1}}
超过100次迭代,即使我将数字限制为1..5,也没有重复/连续的数字。
答案 4 :(得分:2)
MATLAB中的解决方案(也许是不优雅的)可能是这样的:
N = 10;
k = 4;
out = zeros(1,k);
vec = 1 : N;
for idx = 1 : k
ind = randi(numel(vec), 1);
left = max(ind-1, 1); right = min(numel(vec), ind+1);
out(idx) = vec(ind);
to_remove = ind;
if vec(left) == vec(ind)-1
to_remove = [to_remove left];
end
if vec(right) == vec(ind)+1
to_remove = [to_remove right];
end
vec(to_remove) = [];
end
我们首先声明N
和k
,然后声明一个长度为k
的零的输出数组。然后,我们生成一个采样向量vec
,最初从1到N
。接下来,对于我们想要放入输出的每个值,我们生成一个随机位置从矢量中采样,然后从左侧和右侧看一下位置......确保我们位于阵列。此外,如果要删除的索引左侧的值以及右侧的值彼此相等,我们仅向左或向右移除(感谢烧杯!)
我们使用此位置和此向量中的样本,将此位置的值放置到输出,然后从此向量中删除此向量中左侧,右侧和实际索引本身的索引。这消除了再次从这些值中取样的可能性。我们重复这个,直到我们用完输出值。
以下是一些试运行:
>> out
out =
9 7 1 5
>> out
out =
7 1 4 10
>> out
out =
10 8 1 6
>> out
out =
10 4 8 1
答案 5 :(得分:2)
有时候生成的样本比你需要的更快更容易,然后扔掉不想要的值。
一个(慢)的例子。
vec= randi(100,1,1);
for j = 2:50,
while(abs(vec(j)-vec(j-1)<2) vec(j)= randi(100,1,1);end;
end
另一种方式。假设你想要50个样本
vec = rand(100,100,1);
badindex = find(abs(vec(1:99)-vec(2:100) < 1));
vec(badindex) = vec(badindex-1)+vec(badindex+1);
% if you don't want big values,
vec(vec>100) = vec (vec>100) -100; % to ensure, I hope, that neighbors
%是不连续的
(这在R
)中会更容易。
答案 6 :(得分:1)
一个不是特别优雅的python解决方案:
def nonseq(n, k):
out = [random.randint(0, n)]
while len(out) < k:
x = random.randint(0, n)
if abs(x - out[-1]) > 1:
out.append(x)
return out
答案 7 :(得分:1)
我的实施:
def ncsample(population, k):
import random
if k > 0:
i = random.randrange(0, len(population) - 2*(k-1))
return [population[i]] + ncsample(population[i+2:], k-1)
else:
return []
注意:它一次性地随机查找序列(在while循环中没有拒绝采样)。
MATLAB实现:
function r = ncsample(population, k)
if k > 0
i = randi(length(population) - 2*(k-1));
r = [population(i) ncsample(population((i+2):end), k-1)];
else
r = [];
end
end
一些测试:
>> for i=1:10; fprintf('%s\n',sprintf('%d ', ncsample(1:10, 4))); end
1 5 7 9
3 5 8 10
3 5 8 10
4 6 8 10
2 6 8 10
1 4 8 10
1 4 7 9
3 6 8 10
1 6 8 10
2 4 7 9
答案 8 :(得分:1)
这是一个递归的优雅版本,我只是添加了对k和N的检查以避免无限递归,如果k> N / 2没有解决方案存在。
结果保证随机。
import random
def myFunc(N,k):
if k>(N+1)/2:
return "k to big for N"
returnValue = sorted(random.sample(range(1,N+1),k))
toTest = [x - returnValue[i - 1] for i, x in enumerate(returnValue)][1:]
if 1 in toTest:
return myFunc(N,k)
else:
return returnValue
print myFunc(10,4)