2009-03-30 2 views
16

Wenn ich zwei Bereiche, die sich überlappen:(Ruby) Wie überprüfen Sie, ob ein Bereich eine Teilmenge eines anderen Bereichs enthält?

x = 1..10 
y = 5..15 

Wenn ich sage:

puts x.include? y 

der Ausgang ist:

false 

, weil sich die beiden Bereiche nur teilweise überlappen.

Aber wenn ich will, dass es "wahr" ist, wenn es teilweise Überschneidungen zwischen zwei Bereichen gibt, wie würde ich das schreiben? Mit anderen Worten, ich brauche einen Weg zu wissen, wann ein Bereich eine Untermenge eines anderen Bereichs enthält. Ich nehme an, es gibt eine elegante Möglichkeit, dies in Ruby zu schreiben, aber die einzigen Lösungen, die ich mir vorstellen kann, sind ausführlich.

+2

Der Ausgang i s 'false' weil das folgende falsch ist:' x.begin <= y und y <= x.end' --- _not_ weil sie sich nur teilweise überlappen. – Kevin

Antwort

7

Vorsicht dies mit großen Bereichen verwenden, aber dies ist eine elegante Art und Weise, es zu tun:

(x.to_a & y.to_a).empty? 
+0

Dies ist ein O (N) Speicher und Zeit vs @ MarkusQ O (1) Weg. – Barry

+0

Und ist nicht wirklich brauchbar, wenn statt der Nummer * Zeit * reicht;) –

59

Der effizienteste Weg ist es, die Grenzen

(x.first <= y.last) and (y.first <= x.last) 
+0

Warum ist dies effizienter als die Konvertierung in ein Array? Werden beim Konvertieren in ein Array viele Ressourcen benötigt? –

+1

Okay, das ist viel besser als meins! – Angela

+1

Wenn Sie es in ein Array konvertieren, wird ein Array erstellt und mit Werten gefüllt. Anschließend wird dasselbe für das zweite Array ausgeführt. Anschließend werden die beiden Arrays nach übereinstimmenden Elementen durchsucht. setze x = 10000000..20000000 und y = 30000000..40000000 und mal die beiden Methoden um zu sehen was ich meine. – MarkusQ

1

zu vergleichen, wenn ein Programm umfasst, entweder den Anfang oder das Ende eines zweiten Bereichs, dann überlappen sie sich.

(x === y.first) or (x === y.last) 

ist die gleiche wie folgt aus:

x.include?(y.first) or x.include?(y.last) 
+1

Siehe [mein Kommentar] (http://stackoverflow.com/questions/699448/ruby-how-do-you-check-whether-a-range-contains-a-subset-of-another-range#comment19657429_1337373) zu Sehen Sie, warum das nicht stimmt. – jasonkarns

1

Aber wenn ich es will „true“ sein, wenn es teilweise eine Überlappung zwischen zwei Bereichen, wie würde ich das schreiben?

Sie können die Bereiche in ein Array umwandeln und verwenden Sie die & Operator (Verbindung). Dies liefert ein neues Array mit allen Elementen, die in beiden Arrays auftreten. Wenn das resultierende Array nicht leer ist, bedeutet das, dass es einige überlappende Elemente:

def overlap?(range_1, range_2) 
    !(range_1.to_a & range_2.to_a).empty? 
end 
+0

Dies scheint die intuitivste Lösung zu sein. –

+1

@Chris - Es kann "die intuitivste" sein, aber 1) es ist lächerlich ineffizient und 2) es funktioniert nur in ganzzahligen Bereichen, so würde ich nicht empfehlen, es zu verwenden. – MarkusQ

1

Wenn Sie Überlappung sind die Überprüfung, dann würde ich nur

(x.include? y.first) or (x.include? y.last) 

als ein Bereich tun wird müssen mindestens eines der Enden des anderen enthalten. Das ist für mich intuitiver als die angenommene Antwort auf die Conjection, allerdings nicht so effizient wie der Limit-Vergleich von MarkusQ.

+1

Dies deckt nur den Fall ab, in dem sie * teilweise * überlappen. Mit 'x = 3..6' und' y = 1..10' würde Ihr Code false zurückgeben, aber die beiden Bereiche überlappen sich tatsächlich. – jasonkarns

2

Sie auch die Bereiche zu sets umwandeln könnte, sind da Sie im Grunde hier eingestellten Kreuzung zu tun. Könnte einfacher sein, wenn Sie mit mehr als zwei Bereichen zu tun haben.

x = (1..10).to_set 
y = (5..15).to_set 
!(x & y).empty? #returns true (true == overlap, false == no overlap) 
+0

Schöne Technik. – Anwar

-1

Einige hilfreiche enumerable Methoden:

# x is a 'subset' of y 
x.all?{|n| y.include? n} 
# x and y overlap 
x.any?{|n| y.include? n} 
# x and y do not overlap 
x.none?{|n| y.include? n} 
# x and y overlap one time 
x.one?{|n| y.include? n} 
1

Diese Methode kann Überschneidungen zwischen mehreren Bereichen in einer effizienten Art und Weise getestet werden:

def range_overlap?(ranges) 
    sorted_ranges = ranges.sort 
    sorted_ranges.each_cons(2).each do |r1, r2| 
    return true if r2.first <= r1.last 
    end 
    return false 
end 


def test(r) 
    puts r.inspect, range_overlap?(r) 
    puts '================' 
    r = r.reverse 
    puts r.inspect, range_overlap?(r) 
    puts '================' 
end 


test [[1,9], [10, 33]] 
test [[1,10], [5, 8]] 
test [[1,10], [10, 33]] 
0

Rails hat Range#overlaps?

def overlaps?(other) 
    cover?(other.first) || other.cover?(first) 
end