在洗牌和切片Ruby数组时防止相同的对

时间:2017-11-25 15:06:21

标签: arrays ruby shuffle

在Ruby数组中生成一组随机对时,我想阻止生成具有相同项的对。

例如:

[1,1,2,2,3,4].shuffle.each_slice(2).to_a

可能会产生:

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

我希望能够确保它产生如下结果:

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

提前感谢您的帮助!

4 个答案:

答案 0 :(得分:2)

arr = [1,1,2,2,3,4]
loop do
  sliced = arr.shuffle.each_slice(2).to_a
  break sliced if sliced.none? { |a| a.reduce(:==) }
end

答案 1 :(得分:1)

以下是产生所需结果的三种方法(不包括重复采样的方法,直到找到有效的样本)。以下数组将用于说明。

arr = [1,4,1,2,3,2,1]

使用Array#combinationArray#sample

如果允许采样对具有两次相同的数字,则样本空间将为

arr.combination(2).to_a
  #=> [[1, 4], [1, 1], [1, 2], [1, 3], [1, 2], [1, 1], [4, 1], [4, 2],
  #    [4, 3], [4, 2], [4, 1], [1, 2], [1, 3], [1, 2], [1, 1], [2, 3],
  #    [2, 2], [2, 1], [3, 2], [3, 1], [2, 1]]

包含相同值两次的对 - 此处为[1, 1][2, 2] - 不需要,因此可以从上面的数组中删除它们。

sample_space = arr.combination(2).reject { |x,y| x==y }
  #=> [[1, 4], [1, 2], [1, 3], [1, 2], [4, 1], [4, 2], [4, 3],
  #    [4, 2], [4, 1], [1, 2], [1, 3], [1, 2], [2, 3], [2, 1],
  #    [3, 2], [3, 1], [2, 1]]

我们显然要从arr.size/2中抽取sample_space元素。根据是否要完成with or without replacement,我们会写

sample_space.sample(arr.size/2)
  #=> [[4, 3], [1, 2], [1, 3]]

没有替换的采样和

Array.new(arr.size/2) { sample_space.sample }
  #=> [[1, 3], [4, 1], [2, 1]]

用于替换的采样。

每对的样本元素依次为方法1

此方法与下一个方法一样,只能用于替换样本。

让我们首先考虑对一对进行抽样。我们可以通过从arr中随机选择对中的第一个元素来执行此操作,删除arr中该元素的所有实例,然后从arr的左侧对第二个元素进行采样。

def sample_one_pair(arr)
  first = arr.sample
  [first, second = (arr-[first]).sample]
end

要绘制arr.size/2对的样本,我们执行以下操作。

Array.new(arr.size/2) { sample_one_pair(arr) }
   #=> [[1, 2], [4, 3], [1, 2]]

每对的样本元素依次为方法2

这种方法是一种非常快速的方法,可以替换大量的对。与之前的方法一样,它不能用于无需替换的样本。

首先,计算 cdf (累积分布函数)以随机绘制arr元素。

counts = arr.group_by(&:itself).transform_values { |v| v.size }
  #=> {1=>3, 4=>1, 2=>2, 3=>1}

def cdf(sz, counts)
  frac = 1.0/sz
  counts.each_with_object([]) { |(k,v),a|
    a << [k, frac * v + (a.empty? ? 0 : a.last.last)] }
end

cdf_first = cdf(arr.size, counts)
  #=> [[1, 0.429], [4, 0.571], [2, 0.857], [3, 1.0]]

这意味着有一个概率为0.429(四舍五入)随机抽取1,0.571抽取14,0.857抽取{{1} },14和1.0绘制四个数字之一。因此,我们可以通过获取0到1之间的(伪)随机数(2)从arr中随机抽样,然后确定p = rand的第一个元素,{{1} } counts_cdf

[n, q]

在仿真模型中,顺便说一下,这是从离散概率分布生成伪随机变量的标准方法。

在绘制对的第二个随机数之前,我们需要修改p <= q以反映无法再次绘制第一个数字的事实。假设将有许多对随机生成,最有效的是构造一个哈希def draw_random(cdf) p = rand cdf.find { |n,q| p <= q }.first end draw_random(counts_cdf) #=> 1 draw_random(counts_cdf) #=> 4 draw_random(counts_cdf) #=> 1 draw_random(counts_cdf) #=> 1 draw_random(counts_cdf) #=> 2 draw_random(counts_cdf) #=> 3 ,其键是为该对随机绘制的第一个值,其值是相应的cdf。

cdf_first

例如,如果为该对的第一个元素绘制了cdf_second,则为第二个元素cdf_second = counts.keys.each_with_object({}) { |n, h| h[n] = cdf(arr.size - counts[n], counts.reject { |k,_| k==n }) } #=> {1=>[[4, 0.25], [2, 0.75], [3, 1.0]], # 4=>[[1, 0.5], [2, 0.833], [3, 1.0]], # 2=>[[1, 0.6], [4, 0.8], [3, 1.0]], # 3=>[[1, 0.5], [4, 0.667], [2, 1.0]]} 绘制2的概率为0.6绘制10.81绘制41.01

然后我们可以按如下方式对一对进行采样。

4

和以前一样,要使用替换来采样3值,我们执行

def sample_one_pair(cdf_first, cdf_second)
  first = draw_random(cdf_first)
  [first, draw_random(cdf_second[first])]
end

答案 2 :(得分:1)

通过替换,您可能会得到以下结果:

unique_pairs([1, 1, 2, 2, 3, 4]) # => [[4, 1], [1, 2], [1, 3]]

请注意1被选中三次,即使它仅在原始数组中两次。这是因为1被替换了#34;每次选择它。换句话说,它可以放回到集合中,可能会再次被选中。

这是Cary优秀的sample_one_pair解决方案没有替换的版本:

def unique_pairs(arr)
  dup = arr.dup

  Array.new(dup.size / 2) do
    dup.shuffle!

    first = dup.pop
    second_index = dup.rindex { |e| e != first }
    raise StopIteration unless second_index
    second = dup.delete_at(second_index)

    [first, second]
  end
rescue StopIteration
  retry
end

unique_pairs([1, 1, 2, 2, 3, 4]) # => [[4, 3], [1, 2], [2, 1]]

这可以通过创建原始数组的副本并在选择时删除元素(因此无法再次选择)。如果无法生成正确数量的对,则进行救援/重试。例如,如果首先选择[1, 3],然后选择[1, 4],则无法生成三个唯一对,因为[2, 2]就是剩下的所有;样本空间已用完。

这应该比Cary的解决方案(更换时间)慢,但比需要循环和重试的已发布解决方案(无需替换)更快(平均)。 Welp,粉笔另一个指向&#34;总是基准!&#34;关于 所有 我的大多数假设我都错了。以下是我的机器上有16个数字([1, 1, 2, 2, 3, 4, 5, 5, 5, 6, 7, 7, 8, 9, 9, 10])数组的结果:

cary_with_replacement
                         93.737k (± 2.9%) i/s -    470.690k in   5.025734s
mwp_without_replacement
                        187.739k (± 3.3%) i/s -    943.415k in   5.030774s
mudasobwa_without_replacement
                        129.490k (± 9.4%) i/s -    653.150k in   5.096761s

编辑:我已经更新了上述解决方案,以解决Stefan的众多问题。事后看来,错误是显而易见的,令人尴尬!从好的方面来说,修改后的解决方案现在比mudasobwa的解决方案更快,我已经证实这两种解决方案具有相同的偏见。

答案 3 :(得分:-1)

你可以检查是否有任何遗骸并再次洗牌:

takePhoto() {


    const options: CameraOptions = {
      quality: 50,
      targetHeight: 600,
      targetWidth: 600,
      destinationType: this.camera.DestinationType.DATA_URL,
      encodingType: this.camera.EncodingType.JPEG,
      mediaType: this.camera.MediaType.PICTURE
    }

    this.camera.getPicture(options).then((imageData) => {
      let base64Image = 'data:image/jpeg;base64,' + imageData;
      const pictures=storage().ref(`ProfilePictures/${this.myUid}`);
      pictures.putString(base64Image, `data_url`);



     }, (err) => {

      }).present();
     });

  }

不太可能,请注意无限循环的可能性