将范围拆分为X组

时间:2015-10-19 00:04:55

标签: arrays ruby split range

我需要将一个范围拆分为X个组,并且我很难找到一种不使用数组的方法,因为这些范围可能非常大。

我目前的解决方案是创建一个超出范围的数组,然后用一些数学运算调用它上面的each_slice,将数据分成大小相同的X个组,具体取决于有多少组

irb(main):026:0> a = (0..10)
=> 0..10
irb(main):027:0> a.each_slice( (a.size/3.0).round ).to_a
=> [[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10]]
irb(main):028:0> a.each_slice( (a.size/5.0).round ).to_a
=> [[0, 1], [2, 3], [4, 5], [6, 7], [8, 9], [10]]

这个问题是,当范围过大时,应用程序将因为分割数组所需的计算而挂起。

我真正需要的是这种格式的数组(考虑a.size / 3.0 3组示例):

[0..3, 4..7, 8..10]

因此,我可以迭代数组,将它们传递给set_range库中的Net::HTTP方法。

我处理的范围与0..46000000一样大或大,因为我处理的是文件大小(以字节为单位)。

任何帮助都将不胜感激。

3 个答案:

答案 0 :(得分:0)

喜欢这个吗?

def split_ranges(amount, max)
    (0...amount).collect{|i| (i * max / amount)...((i+1) * max / amount)}
end

p split_ranges(3, 46000000)

输出:

[0...15333333, 15333333...30666666, 30666666...46000000]

编辑:(OP请求)

def split_ranges(amount, max)
    (0...amount).collect{|i| (i * (max + 1) / amount)..((i + 1) * (max + 1) / amount - 1)}
end

p split_ranges(3, 46000000)

输出:

[0..15333332, 15333333..30666666, 30666667..46000000]

答案 1 :(得分:0)

class Range
  def each_subrange(n)
    return to_enum(:each_subrange, n) unless block_given?

    range_size = size
    range_begin = self.begin
    n.times do |i|
      yield range_begin + range_size * i / n .. range_begin + range_size * (i + 1) / n - 1
    end
  end
end

a = 0..46000000
# without a block
puts a.each_subrange(3).to_a
# with a block
a.each_subrange(3) do |r|
  puts r
end

答案 2 :(得分:0)

这将确保没有范围与任何其他范围的大小相差多于一个:

def split_it(r, n)
  return [r] if n == 1
  last = r.first - 1 + (r.last-r.first+1)/n
  [r.first..last].concat(split_it(last+1..r.last, n-1))
end

r = 0..46000000

split_it(r, 3)
  #=> [0..15333332, 15333333..30666666, 30666667..46000000] 
split_it(r, 3).map(&:size)
  #=> [15333333, 15333334, 15333334] 

split_it(r, 4)
  #=> [0..11499999, 11500000..22999999, 23000000..34499999,
  #    34500000..46000000] 
split_it(r, 4).map(&:size)
  # => [11500000, 11500000, 11500000, 11500001] 

split_it(r, 5)
  #=> [0..9199999, 9200000..18399999, 18400000..27599999,
  #    27600000..36799999, 36800000..46000000] 
split_it(r, 5).map(&:size)
  #=> [9200000, 9200000, 9200000, 9200000, 9200001]