检测Ruby中的重叠范围

时间:2015-01-19 18:06:07

标签: ruby-on-rails ruby arrays

我有一系列范围:

[[39600..82800], [39600..70200],[70200..80480]]

我需要确定是否存在重叠。在ruby中这样做的简单方法是什么?

在上述情况下,输出应为'重叠'。

4 个答案:

答案 0 :(得分:8)

这是一个非常有趣的谜题,特别是如果你关心表演。

如果范围只有两个,那么这是一个相当简单的算法,ActiveSupport overlaps?扩展也包含了这个算法。

def ranges_overlap?(r1, r2)
  r1.cover?(r2.first) || r2.cover?(r1.first)
end

如果你想比较多个范围,那么这是一个相当有趣的算法练习。

您可以遍历所有范围,但是您需要将每个范围与所有其他可能性进行比较,但这是一种具有指数成本的算法。

更有效的解决方案是order the ranges并执行二进制搜索,或使用数据结构(例如树)来计算重叠。

Interval tree页面中也解释了此问题。计算重叠主要包括找到树的交叉点。

答案 1 :(得分:3)

考虑一下:

class Range
  include Comparable

  def <=>(other)
    self.begin <=> other.begin
  end

  def self.overlap?(*ranges)
    edges = ranges.sort.flat_map { |range| [range.begin, range.end] }
    edges != edges.sort.uniq
  end
end

Range.overlap?(2..12, 6..36, 42..96) # => true

备注

  1. 这可以包含任意数量的范围。
  2. 查看gist并使用代码进行一些测试。
  3. 代码创建一个 flat 数组,其中包含每个范围的开头和结尾。
  4. 如果不重叠,此数组将保留订单。 (用一些例子比用文字解释原因更容易想象,试试吧。)

答案 2 :(得分:2)

这不是一种方法吗?

def any_overlapping_ranges(array_of_ranges)
   array_of_ranges.sort_by(&:first).each_cons(2).any?{|x,y|x.last>y.first}
end

p any_overlapping_ranges([50..100, 1..51,200..220]) #=> True

答案 3 :(得分:1)

为了简单和可读性,我建议采用这种方法:

def overlaps?(ranges)
  ranges.each_with_index do |range, index|
    (index..ranges.size).each do |i|
      nextRange = ranges[i] unless index == i
      if nextRange and  range.to_a & nextRange.to_a 
        puts "#{range} overlaps with #{nextRange}"
      end
    end
  end
end


r = [(39600..82800), (39600..70200),(70200..80480)]
overlaps?(r)

和输出:

ruby ranges.rb 
39600..82800 overlaps with 39600..70200
39600..82800 overlaps with 70200..80480
39600..70200 overlaps with 70200..80480