【问题标题】:generate non-consecutive samples生成非连续样本
【发布时间】:2015-09-12 14:08:14
【问题描述】:

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

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

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

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

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

【问题讨论】:

  • 不,当然不能保证; 这是随机的
  • @A.M.这两个例子不足以说明你的要求
  • @A.M. - 啊。好,很好。那正是我所想。您标记了 Python 和 MATLAB。你更喜欢哪一个?
  • @ThePredator:有些东西可能是随机生成但满足约束条件的。 OP 没有要求特定的分布,这里可能有多个答案,分布略有不同,具体取决于它的生成方式。
  • @NeilSlater :如果您对它们进行排序,它们将是连续的。我已经编辑了问题并写道每对样本之间的差异大于 1

标签: matlab random


【解决方案1】:

您可以使样本之间的增量均匀分布在 2 和 N-1 之间(以避免连续和重复的数字):

N=10;
k=4;
increments = floor(rand(1,k)*(N-2))+2  %// increments allowed are from 2 to N-1 inclusive
out = mod(cumsum(increments), N)+1   %// sum increments

在 python 中相同:

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.]

超过 100 次迭代,即使我将数字限制为 1..5,也没有重复/连续的数字。

【讨论】:

  • mod 函数生成重复样本!
  • 是的,但重复不是连续的。如果您不想重复,则将增量从 2 增加到 N-1。
  • 我遇到过你的方法的反例。仍然是连续样本和重复样本。
  • 我没有得到任何连续的数字。我编辑了增量也丢弃了重复的数字。
  • 谢谢。当我运行编辑后的代码时,我没有得到连续的样本,但我仍然得到重复的样本,我猜这可能会被忽略。
【解决方案2】:

不是特别优雅的 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

【讨论】:

    【解决方案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]
    

    编辑:提高效率

    【讨论】:

    • 这个算法有可能会卡住。对于 N=3,k=2,唯一的解决方案是 [1,3]。如果运气不好先选2,找不到解决办法。
    【解决方案4】:

    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
    

    【讨论】:

    • 我可以在这里看到一个问题,使用vec([left ind right]) = [];,您最终可能会从采样数组中删除不连续的元素。例如。如果第一个样本是 5,则删除 4,5,6 。 . .但是下一个样本是 3 你删除了 2,3,7!所以从技术上讲,它满足了约束条件,但它不能生成许多可能的解决方案。
    • @NeilSlater - 哦,多么真实......好吧。我得考虑一下。感谢现场。
    • @NeilSlater 我想出了如何解决它。只需要几个 if 语句,但我会在今晚晚些时候修复它。现在做不到!
    • 您所要做的就是在删除之前确保vec(left) == vec(ind)-1vec(right) == vec(ind)+1
    • 酷豆...虽然我认为你已经有了这个想法。我们的cmets同时发布。 :)
    【解决方案5】:

    我的实现:

    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 
    

    【讨论】:

      【解决方案6】:

      S 表示所有k 元素向量的集合,其值取自[1,...,N],没有任何连续值。要在 S 上以 均匀 分布随机抽样,您可以使用拒绝方法:

      1. 在更大的采样空间上均匀采样,T
      2. 如果样本属于目标区域S,则接受该样本。否则返回第 1 步(样本被拒绝)。

      在 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
      

      【讨论】:

      • 方法简单且分布均匀的优点是不错,但这会因 k 适度大时性能不佳,尤其是在接近任何特定 N 的 k 极限时。
      • 确实如此。这对于高 N 和 K=(N+1)/2 可能非常低效
      • 同意。拒绝方法可能效率低下
      • 如果您没有找到好的技巧(请参阅我的回答),拒绝是此类问题的唯一可能性。我在这里看到的大多数其他答案都试图逐个元素地创建解决方案,导致不同解决方案的概率略有不同。
      【解决方案7】:

      这是一个递归的优雅版本,我只是添加了对 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)
      

      【讨论】:

      • 这段代码拒绝 N=3, k=2,但是 [1,3] 是一个解决方案。我认为如果k&gt;(N+1)/2 是不可能的
      • 我的错,无论如何,最佳答案要好得多,无论如何我都会纠正它。
      【解决方案8】:

      有时生成比您需要的更多的样本然后丢弃不需要的值会更快、更容易。

      一个(慢)示例。

      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 中会更容易)。

      【讨论】:

      • Plus 1 - 在 R 中绝对更容易。
      【解决方案9】:
      sort(randperm(N-(k-1),k))+[0:(k-1)]
      

      这个解决方案背后有一个简单的观察,如果你对你的问题采取任何排序的解决方案并减去[0:(k-1)],你最终会从N-(k-1)中随机选择k个数字

      【讨论】:

      • 这太聪明了。谢谢:)
      • 顺便说一下,在python中:map(operator.add, sorted(random.sample(range(1,N-k+2),k)),range(0,k))
      • @Daniel 好想!
      • numpysorted(numpy.random.choice(N-(k-1), k, replace=False)) + numpy.arange(k) + 1。没有:[x + i + 1 for i, x in enumerate(sorted(random.sample(range(N-(k-1)), k)))].
      • @CarlWitthoft:没错,我完全跳过了 QA 到底是什么的讨论,因为当我写这篇文章时,Luis Mendo 的回答已经被接受了。假设这是想要的,所以我只是提供了一个更有效的版本。
      猜你喜欢
      • 2013-03-03
      • 2018-06-13
      • 1970-01-01
      • 2011-09-23
      • 1970-01-01
      • 1970-01-01
      • 2021-03-28
      • 1970-01-01
      • 2017-03-20
      相关资源
      最近更新 更多