生成非连续样本

时间:2015-06-25 19:37:40

标签: matlab random

我们如何有效地从k生成[1,...,N]个随机和非连续样本?

(N=10, k=4)的非期望示例: 2,3,8,10

这不是一个理想的例子,因为23是连续的。

(N=10, k=4)的所需示例: 2,6,8,10

这是一个很好的例子,因为每对样本之间的差异大于1

9 个答案:

答案 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 统一分布进行随机抽样,您可以使用拒绝方法:

  1. 在较大的样本空间上均匀采样, T
  2. 如果样本属于目标区域 S ,请接受样本。否则返回步骤1(样品被拒绝)。
  3. 在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

我们首先声明Nk,然后声明一个长度为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)