2009-03-30 10 views
16

Jeśli mam dwa zakresy, które pokrywają:(Ruby) Jak sprawdzić, czy zakres zawiera podzestaw innego zakresu?

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

Kiedy mówię:

puts x.include? y 

wyjście to:

false 

bo tylko dwa zakresy pokrywają się częściowo.

Ale jeśli chcę, aby było "prawdziwe", gdy występuje częściowe nachodzenie na siebie dwóch zakresów, jak bym to napisał? Innymi słowy, potrzebuję sposobu, aby wiedzieć, kiedy jeden zakres zawiera podzbiór innego zakresu. Zakładam, że istnieje elegancki sposób na napisanie tego w Ruby, ale jedyne rozwiązania, jakie mogę wymyślić, są pełne.

+2

Wyjście i s "fałsz", ponieważ następujące wyrażenie to fałsz: 'x.begin <= yi y <= x.end' --- _not_, ponieważ tylko częściowo pokrywają się. – Kevin

Odpowiedz

7

Bądź ostrożny używając tego z dużych zakresów, ale jest to elegancki sposób to zrobić:

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

To jest pamięć O (N) i czas względem @ MarkusQ's O (1). – Barry

+0

I nie nadaje się do użytku, gdy zamiast numeru masz * zakresy czasu *;) –

59

Sprawne sposobem jest porównanie limitów

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

Dlaczego jest to bardziej wydajne niż konwersja do tablicy? Czy konwersja do tablicy wykorzystuje dużo zasobów? –

+1

Dobra, to jest o wiele lepsze niż moje! – Angela

+1

Konwersja do tablicy tworzy tablicę i wypełnia ją wartościami, a następnie robi to samo dla drugiej tablicy, a następnie przeszukuje dwie tablice pod kątem pasujących elementów. set x = 10000000..20000000 i y = 30000000..40000000 i czas dwóch metod, aby zobaczyć co mam na myśli. – MarkusQ

1

Jeżeli oferta obejmuje zarówno początek lub koniec drugiego zakresu, wtedy się pokrywać.

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

jest taki sam jak ten:

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

Zobacz [mój komentarz] (http://stackoverflow.com/questions/699448/ruby-how-do-you-check-whether-a-range-contains-a-ubset-another-range#comment19657429_1337373) na zobacz, dlaczego to nie jest prawda. – jasonkarns

1

Ale jeśli ma to być „true”, gdy nie jest częściowe nakładanie się dwóch zakresach, jak chciałbym napisać, że?

można przekonwertować zakresy do tablicy, i użyć operatora & (połączeniową). Zwraca to nową tablicę ze wszystkimi elementami występującymi w obu tablicach. Jeśli uzyskany tablica nie jest pusty, to znaczy, że istnieje kilka nakładających się elementów:

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

To wydaje się najbardziej intuicyjne rozwiązanie. –

+1

@Chris - Może to być "najbardziej intuicyjne", ale 1) jest śmiesznie nieefektywne i 2) działa tylko na zakresach całkowitych, więc nie radziłbym go używać. – MarkusQ

1

jeśli jesteś sprawdzanie do nakładania, to bym po prostu zrobić

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

jako jeden zakres będzie muszą zawierać co najmniej jeden koniec drugiego. Jest to dla mnie bardziej intuicyjne niż akceptowana odpowiedź na połączenie, choć nie tak wydajna, jak porównanie limitów Markusa.

+1

To obejmuje tylko przypadek, w którym * częściowo * pokrywają się. Biorąc pod uwagę 'x = 3..6' i' y = 1..10', twój kod zwróciłby fałsz, ale te dwa zakresy rzeczywiście pokrywają się. – jasonkarns

2

Można również konwertować zakresy do sets, ponieważ jesteś w zasadzie robi ustawić skrzyżowanie tutaj. Może być łatwiej, jeśli masz do czynienia z więcej niż dwoma zakresami.

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

Fajna technika. – Anwar

-1

niektórych osób metody przeliczalne:

# 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

Ta metoda może być stosowana do badania nakładania się wielu przedziałów w efektywny sposób:

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]] 
Powiązane problemy