Одно из решений (в дополнение ко всем другим решениям, представленным здесь) состоит в использовании дерева интервалов/сегментов (на самом деле это одно и то же):
http://en.wikipedia.org/wiki/дерево_сегментов
http://en.wikipedia.org/wiki/Interval_tree
Одним из больших преимуществ такого подхода является простота выполнения произвольных логических операций (а не только вычитания) с использованием одного и того же фрагмента кода. У де Берга есть стандартная трактовка этой структуры данных. Чтобы выполнить любую логическую операцию над парой деревьев интервалов (включая вычитание), вы просто объединяете их вместе. Вот некоторый (по общему признанию наивный) код Python для выполнения этого с несбалансированными деревьями диапазонов. Тот факт, что они несбалансированы, не влияет на время, необходимое для слияния деревьев, однако построение дерева здесь - действительно глупая часть, которая в конечном итоге становится квадратичной (если только сокращение не выполняется путем разбиения, в чем я почему-то сомневаюсь). В любом случае, вот:
class IntervalTree:
def __init__(self, h, left, right):
self.h = h
self.left = left
self.right = right
def merge(A, B, op, l=-float("inf"), u=float("inf")):
if l > u:
return None
if not isinstance(A, IntervalTree):
if isinstance(B, IntervalTree):
opT = op
A, B, op = B, A, (lambda x, y : opT(y,x))
else:
return op(A, B)
left = merge(A.left, B, op, l, min(A.h, u))
right = merge(A.right, B, op, max(A.h, l), u)
if left is None:
return right
elif right is None or left == right:
return left
return IntervalTree(A.h, left, right)
def to_range_list(T, l=-float("inf"), u=float("inf")):
if isinstance(T, IntervalTree):
return to_range_list(T.left, l, T.h) + to_range_list(T.right, T.h, u)
return [(l, u-1)] if T else []
def range_list_to_tree(L):
return reduce(lambda x, y : merge(x, y, lambda a, b: a or b),
[ IntervalTree(R[0], False, IntervalTree(R[1]+1, True, False)) for R in L ])
Я написал это быстро и не так много тестировал, поэтому могут быть ошибки. Также обратите внимание, что этот код будет работать с произвольными логическими операциями, а не только с различиями (вы просто передаете их в качестве аргумента операции при слиянии). Временная сложность оценки любого из них линейна по размеру выходного дерева (что также совпадает с количеством интервалов в результате). В качестве примера я запустил его в случае, который вы предоставили:
#Example:
r1 = range_list_to_tree([ (1, 1000), (1100, 1200) ])
r2 = range_list_to_tree([ (30, 50), (60, 200), (1150, 1300) ])
diff = merge(r1, r2, lambda a, b : a and not b)
print to_range_list(diff)
И я получил следующий вывод:
[(1, 29), (51, 59), (201, 1000), (1100, 1149)]
Что, кажется, согласуется с тем, что вы ожидаете. Теперь, если вы хотите выполнить некоторые другие логические операции, вот как это будет работать, используя ту же функцию:
#Intersection
merge(r1, r2, lambda a, b : a and b)
#Union
merge(r1, r2, lambda a, b : a or b)
#Xor
merge(r1, r2, lambda a, b : a != b)
person
Mikola
schedule
24.06.2011
r2
не находится вr1
? - person JBernardo   schedule 24.06.2011r1=(1.5, 5.1)
иr2 = (2.5, 4.3)
? Как именно мы должны поступить с этим? - person jmlopez   schedule 24.06.2011