2009-03-30 9 views
16

Si tengo dos rangos que se solapan:(Ruby) ¿Cómo se comprueba si un rango contiene un subconjunto de otro rango?

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

cuando digo:

puts x.include? y 

la salida es:

false 

porque los dos rangos sólo se solapan parcialmente.

Pero si quiero que sea "verdadero" cuando hay una superposición parcial entre dos rangos, ¿cómo escribiría eso? En otras palabras, necesito una forma de saber cuándo un rango contiene un subconjunto de otro rango. Supongo que hay una forma elegante de escribir esto en Ruby, pero las únicas soluciones que puedo pensar son detalladas.

+2

La salida i s 'falso' porque lo siguiente es falso:' x.begin <= y y y <= x.end' --- _not_ porque solo se superponen parcialmente. – Kevin

Respuesta

7

Tenga cuidado al usar esto con grandes rangos pero esta es una manera elegante de hacerlo:

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

Esta es una O (N) memoria y tiempo vs @ MarkusQ O (1) camino. – Barry

+0

Y no es realmente útil cuando en lugar de número tiene rangos * time *;) –

59

La forma más eficiente es comparar los límites

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

¿Por qué es esto más eficiente que convertir a una matriz? ¿La conversión a una matriz utiliza muchos recursos? –

+1

De acuerdo, ¡esta es mucho mejor que la mía! – Angela

+1

Al convertirlo en una matriz, se crea una matriz y se rellena con valores, luego se hace lo mismo con la segunda matriz y luego se busca en las dos matrices los elementos coincidentes. establezca x = 10000000..20000000 e y = 30000000..40000000 y cronometra los dos métodos para ver lo que quiero decir. – MarkusQ

1

Si una gama incluye ya sea al principio o al final de un segundo rango, a continuación, se superponen.

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

es lo mismo que esto:

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

Ver [mi comentario] (http://stackoverflow.com/questions/699448/ruby-how-do-you-check-whether-a-range-contains-a-subset-of-another-range#comment19657429_1337373) para ver por qué eso no es verdad – jasonkarns

1

Pero si yo quiero que sea "verdadera" cuando hay una superposición parcial entre dos rangos, ¿Cómo voy a escribir eso?

puede convertir los rangos a una matriz, y utilizar el operador & (conjunción). Esto devuelve una nueva matriz con todos los elementos que se encuentran en ambas matrices. Si la matriz resultante no está vacía, eso quiere decir, que hay algunos elementos superpuestos:

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

Esta parece ser la solución más intuitiva. –

+1

@Chris - Puede ser "el más intuitivo" pero 1) es ridículamente ineficiente y 2) solo funciona en rangos enteros, por lo que no aconsejaría usarlo. – MarkusQ

1

Si usted está comprobando la superposición, entonces yo sólo hago

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

como una gama voluntad debe incluir al menos uno de los extremos del otro. Esto es más intuitivo para mí que la respuesta de conjunción aceptada, aunque no tan eficiente como la comparación de límites de MarkusQ.

+1

Esto solo cubre el caso en que se superponen * parcialmente *. Dado 'x = 3..6' y' y = 1..10', su código devolvería falso, pero los dos rangos efectivamente se superponen. – jasonkarns

2

También podría convertir a los rangos sets, ya que estás haciendo básicamente intersección establecido aquí. Podría ser más fácil si tiene más de dos rangos.

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

Técnica agradable. – Anwar

-1

Algunos métodos enumerables votos:

# 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

Este método puede ser utilizado para probar superposición entre varios rangos de una manera eficiente:

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 tiene Range#overlaps?

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