Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/arrays/12.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ruby 如何对数组进行切片,同时避免每个切片中出现重复值?_Ruby_Arrays_Random Sample - Fatal编程技术网

Ruby 如何对数组进行切片,同时避免每个切片中出现重复值?

Ruby 如何对数组进行切片,同时避免每个切片中出现重复值?,ruby,arrays,random-sample,Ruby,Arrays,Random Sample,假设我有这个数组: a = [1,2,3,3,3,3,3,3,3,3,3,4,5,6,6,7,8,9,10,11] a、 每个_片(2).to_a将生成对,但这些对将包含非唯一值,如[3,3]。所以我想我在寻找某种独特的每片方法 我想要的是能够一直洗牌这个数组,直到我得到一个点,我有唯一的2对(不一定是2,可以是任何东西),就像这样(使用2作为示例): 如果在此数组上执行每个_切片(2),将得到唯一的对: [[3, 1], [3, 7], [6, 3], [4, 5], [8, 3], [9,

假设我有这个数组:

a = [1,2,3,3,3,3,3,3,3,3,3,4,5,6,6,7,8,9,10,11]
a、 每个_片(2).to_a将生成对,但这些对将包含非唯一值,如[3,3]。所以我想我在寻找某种独特的每片方法

我想要的是能够一直洗牌这个数组,直到我得到一个点,我有唯一的2对(不一定是2,可以是任何东西),就像这样(使用2作为示例):

如果在此数组上执行每个_切片(2),将得到唯一的对:

[[3, 1], [3, 7], [6, 3], [4, 5], [8, 3], [9, 3], [2, 3], [6, 3], [3, 11], [10, 3]]
与原始版本相比,您有:

[[1, 2], [3, 3], [3, 3], [3, 3], [3, 3], [3, 4], [5, 6], [6, 7], [8, 9], [10, 11]]
每对中都有非唯一对,如[3,3]

另一个例子,假设我有:

a = [1,2,3,3,3,3,3,3,3,3,3,4,5,6,6,7,8,9,10,11,12,13,14,15,16,17]
现在,假设有一个函数a.unique_slices_of(3),我会得到:

[[4, 16, 3], [1, 9, 3], [3, 6, 17], [3, 6, 10], [15, 3, 2], [3, 8, 12], [11, 3, 14], [7, 13, 3], [3, 5]]
我所说的“唯一切片”是指相同数字不会重复两次的切片:[1,2,3]是唯一切片,[3,1,3]不是唯一切片

到目前为止,我采用了以下方法,似乎需要多次迭代才能使事情正确:

class Array
  def unique_slices_of!(slices)
    loop do
      unique = true
      self.each_slice(slices) do |slice|
        if slice != slice.uniq
          self.shuffle!
          unique = false # so we know whether to loop again
          break
        end
      end
      break if unique # if unique didn't change, that means all slices were equal
      if unique == false then unique == true end # reset and start again
    end
    self 
  end
end
我的代码的主要问题是a)我不认为我使用的是某种惯用的Ruby方法可以将这个过程缩短一半或更多。b) 如果数组不能包含唯一的切片,则可能出现无限循环。我可能需要在这里使用一些组合理论,但我不确定如何使用。

采样独特的组合 如果您正在寻找更惯用的方法,并且算法的效率不是您最关心的问题,那么您可以尝试以下方法:

a = [1, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 5, 6, 6, 7, 8, 9, 10, 11]
a.combination(2).reject { |pair| pair[0] == pair[1] }.sample(a.size / 2)
这种方法的主要缺点是,当a很大时,速度很快,因为在使用和筛选结果之前,会生成所有可能的组合。然而,对于中等大小的阵列来说,它显然已经足够快了

评估解决方案的性能 随机测试表明,对于中等大小的阵列来说,这已经足够快了。考虑:

require 'benchmark'

a = [1, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 5, 6, 6, 7, 8, 9, 10, 11]

Benchmark.measure do
  a.combination(2).reject { |pair| pair[0] == pair[1] }.sample(a.size / 2)
end.to_s
#=> "  0.000000   0.000000   0.000000 (  0.000052)\n"
array = [1, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 5, 6, 6, 7, 8, 9, 10, 11]
element_size = 4 

array.combination(element_size).
  reject { |element| element.map { |member| element.count(member) > 1 }.any? }.
      sample(array.size / element_size)
即使是100000次迭代,在我的系统上也只花了3.650299秒。考虑到你发布的语料库,这似乎足够快,但你的里程数可能会有所不同

允许比较任意子数组大小 将成员与计数进行比较 在评论中,OP询问这是否可以推广到每个包含2、3或4个元素的winnow子数组。是的,通过一些重构,虽然性能会随着组合中元素数量的增加而降低。考虑:

require 'benchmark'

a = [1, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 5, 6, 6, 7, 8, 9, 10, 11]

Benchmark.measure do
  a.combination(2).reject { |pair| pair[0] == pair[1] }.sample(a.size / 2)
end.to_s
#=> "  0.000000   0.000000   0.000000 (  0.000052)\n"
array = [1, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 5, 6, 6, 7, 8, 9, 10, 11]
element_size = 4 

array.combination(element_size).
  reject { |element| element.map { |member| element.count(member) > 1 }.any? }.
      sample(array.size / element_size)
这将使用所需的元素大小来确定要动态采集的样本数。这样做的另一个好处是删除任何部分填充的数组,从而消除了可能出现的“悬空”元素

这里的主要工作仍然是reject方法,它现在使用迭代每个子数组的每个成员,并拒绝在该子数组中多次出现成员的元素。即使使用更好的变量名,也比使用固定的元素大小时更难理解,但它肯定更灵活

更具可读性(且速度稍快)的比较 有了@pguardiario(请参阅)的帽子提示,您甚至可以通过只选择所有数组成员所在的子数组来将其缩短一点,并使其更具可读性。例如:

array.combination(element_size).
  select { |subarray| subarray == subarray.uniq }.
    sample(array.size / element_size)

我有一个似乎有效的解决方案。基本思想是将具有最大计数的元素分布到尽可能多的片中。添加一些
shuffle
,使其看起来随机

class Array
  def unique_slices_of(slice_length)
    buf = []
    arr = []
    hash = Hash.new 0
    self.each {|i| hash[i] += 1}
    sorted = hash.sort_by {|k, v| v}.reverse
    # sorted[][0] holds the element and sorted[][1] holds the count
    return nil if sorted[0][1] > ((self.length * 1.0) / slice_length).ceil
    index = 0
    until sorted.length.zero?
      # Add element to buf and decrement count
      # if count == 0, remove the entry from sorted
      buf << sorted[index][0]
      sorted[index][1] -= 1
      if sorted[index][1] == 0
        sorted.delete_at index
        break if sorted.length == 0
        index -= 1
      end
      index = (index + 1) % sorted.length
      if buf.length == slice_length
        arr << buf.shuffle
        buf.clear
        index = 0
      end
    end
    arr << buf.shuffle if buf.length > 0
    arr.shuffle
  end
end
您可以通过以下方式测试切片是否“唯一”:

所以现在你只需洗牌,直到得到你想要的:

a.shuffle! until a.each_slice(2).all?{|x| x == x.uniq}
避免无限循环的最简单方法是使用
timeout

require 'timeout'
# raise an error if it takes more than 1 second
timeout(1){ a.shuffle! until a.each_slice(3).all?{|x| x == x.uniq} }
一种非随机的方法 这里的想法是将不同的值放入容器中。然后,在剩余垃圾箱的情况下:

  • 按大小订购垃圾箱,首先订购最大的垃圾箱
  • 通过为每个第一个
    max\u slice\u size
    箱子取一个数字来制作切片
  • 移走空箱子
由于切片中的每个值都来自不同的存储箱,因此可以保证切片将包含不同的值

守则:

def slices_without_repeats(a, max_slice_size)
  slices = []
  bins = a.group_by { |e| e }.values
  until bins.empty?
    bins = bins.sort_by(&:size).reverse
    slice_size = [max_slice_size, bins.size].min
    slice = slice_size.times.map do |i|
      bins[i].pop
    end
    slices << slice
    bins.reject!(&:empty?)
    if slice.size < max_slice_size && !bins.empty?
      raise ArgumentError, "An element repeats too much"
    end
  end
  slices
end
当无法执行时,它会检测:

p slices_without_repeats(a, 3)
# An element repeats too much (ArgumentError)
它处理最后一个切片未满的情况:

p slices_without_repeats([1, 2, 3, 3, 4, 4, 4], 3)
# [[4, 3, 2], [4, 3, 1], [4]]

输出是否必须是随机的,或者如果您得到一个这样的数组,即使它是系统计算的数组,它是否足够?长度不必是倍数。关于随机性/系统计算的,没关系。这属于@meagar,我认为这属于灰色区域。尽管有标题,OP实际上是在问,“我想实现一个算法……我尝试了以下方法,随机选择序列,但解决一些问题可能需要很长时间。”这类问题在这方面相当常见。我建议你将标题改为,“重新排列数组,使每个_片生成具有唯一元素的数组”。随机生成可能的解决方案是一种方法,但正如您上面所说,这不是一种要求。您还可以将“洗牌”替换为“排列元素”“。是否可以修改此代码,使其适用于3、4等组合?@daremkd我添加了一节,以满足您对每个子数组大于两个元素的需求。希望有帮助!是的,这真的很有帮助,谢谢。不过,我在使用.composition和循环until.shuffle之间做了一个基准测试!做它的工作(pguardirio逻辑)并且每次都使用.shuffle!do-it的工作比尝试手动提出所有组合要快(而且使用更大的阵列等会变得更糟)。非常感谢你的努力,我竖起了大拇指。哇,那真是太好了。竖起大拇指!问题是,是否有一些数学方法可以首先确定数组是否可以唯一地“切片”,因此,如果数组不能唯一地切片,我会得到一个nil或引发一个错误,而不是使用超时方法。我肯定有,但我不喜欢数学:(我试着提出一些算法,但是有太多的边缘情况,我猜timeout可以做到,prob可以返回数组本身还是arr
a = [1,2,3,3,3,3,3,3,3,3,3,4,5,6,6,7,8,9,10,11]
p slices_without_repeats(a, 2)
# [[3, 6], [3, 9], [3, 7], [3, 2], [3, 6],
#  [3, 10], [3, 11], [3, 4], [1, 8], [3, 5]]
p slices_without_repeats(a, 3)
# An element repeats too much (ArgumentError)
p slices_without_repeats([1, 2, 3, 3, 4, 4, 4], 3)
# [[4, 3, 2], [4, 3, 1], [4]]