随机抽样数组的唯一子集

时间:2012-01-19 16:33:49

标签: ruby random sampling

如果我有一个数组:

a = [1,2,3]

如何随机选择数组的子集,以使每个子集的元素都是唯一的?也就是说,对于a,可能的子集将是:

[]
[1]
[2]
[3]
[1,2]
[2,3]
[1,2,3]

我无法生成所有可能的子集,因为a的实际大小非常大,因此有许多子集。目前,我正在使用“随机游走”的想法 - 对于a的每个元素,我'翻转一个硬币'并包括它,如果硬币出现在头上 - 但我不确定这是否实际上均匀地对空间进行采样。 感觉喜欢它偏向中间,但这可能只是我的想法进行模式匹配,因为会有更多中等大小的可能性。

我使用正确的方法,或者我应该如何随机抽样?

(我知道这更像是一种语言不可知和'数学'问题,但我觉得它不是Mathoverflow的真正材料 - 我只需要一个实际的答案。)

5 个答案:

答案 0 :(得分:5)

继续你原来的“硬币翻转”的想法。它统一地描绘了可能性的空间。

你觉得它偏向于“中间”,但这是因为“中间”的可能性数量最多。想一想:只有1种可能没有元素,只有1种含有所有元素。有N个可能性有1个元素,N个可能有(N-1)个元素。随着所选元素的数量越来越接近(N / 2),可能性的数量会迅速增长。

答案 1 :(得分:1)

您可以生成随机数,将它们转换为二进制数,然后从原始数组中选择位为1的元素。以下是Array类的猴子补丁的实现:

class Array
  def random_subset(n=1)
    raise ArgumentError, "negative argument" if n < 0
    (1..n).map do
      r = rand(2**self.size)
      self.select.with_index { |el, i| r[i] == 1 }
    end
  end
end

用法:

a.random_subset(3) 
#=> [[3, 6, 9], [4, 5, 7, 8, 10], [1, 2, 3, 4, 6, 9]]

通常情况下这不会很糟糕,它是O(n * m),其中n是你想要的子集数,m是数组的长度。

答案 2 :(得分:0)

a.select {|element| rand(2) == 0 }

对于每个元素,翻转硬币。如果是头(== 0),则选择它。

答案 3 :(得分:0)

我认为硬币翻转很好。

ar = ('a'..'j').to_a
p ar.select{ rand(2) == 0 }

具有10个元素的数组具有2 ** 10种可能的组合(包括[]和所有10个元素),其不超过10次(1或0)。它输出更多的四个,五个和六个元素的数组,因为在powerset中有更多的元素。

答案 4 :(得分:0)

从电源组中选择随机元素的方法如下:

my_array = ('a'..'z').to_a
power_set_size = 2 ** my_array.length
random_subset = rand(power_set_size)
subset = []
random_subset.to_i(2).chars.each_with_index do |bit, corresponding_element|
  subset << my_array[corresponding_element] if bit == "1"
end

这使用字符串函数而不是使用真正的“位”和按位操作只是为了方便我。您可以使用实际位将其转换为更快(我猜)的算法。

它的作用是将array的powerset编码为02 ** array.length之间的整数,然后随机选择其中一个整数(确实是随机的)。然后它使用位掩码将整数解码回array的特定子集(1 =元素在子集中,0 =它不是)。

通过这种方式,您可以在阵列的电源组上进行均匀分布。