У меня есть класс, представляющий интервал. Этот класс имеет два свойства: "start" и "end" сопоставимого типа. Теперь я ищу эффективный алгоритм для объединения множества таких интервалов.
Спасибо заранее.
У меня есть класс, представляющий интервал. Этот класс имеет два свойства: "start" и "end" сопоставимого типа. Теперь я ищу эффективный алгоритм для объединения множества таких интервалов.
Спасибо заранее.
Отсортируйте их по одному из терминов (например, начало), затем проверьте перекрытие со своим (правым) соседом при перемещении по списку.
class tp():
def __repr__(self):
return '(%d,%d)' % (self.start, self.end)
def __init__(self,start,end):
self.start=start
self.end=end
s=[tp(5,10),tp(7,8),tp(0,5)]
s.sort(key=lambda self: self.start)
y=[ s[0] ]
for x in s[1:]:
if y[-1].end < x.start:
y.append(x)
elif y[-1].end == x.start:
y[-1].end = x.end
Используйте алгоритм развертки. В основном вы сортируете все значения в списке (сохраняя, начинаются ли они или заканчиваются интервал вместе с каждым элементом). Эта операция O (n log n). Затем вы выполняете цикл за один проход по отсортированным элементам и вычисляете интервалы O (n).
O (n log n) + O (n) = O (n log n)
Алгоритм с помощью geocar терпит неудачу, если:
s=[tp(0,1),tp(0,3)]
Я не очень уверен, но я думаю, что это правильный способ:
class tp():
def __repr__(self):
return '(%.2f,%.2f)' % (self.start, self.end)
def __init__(self,start,end):
self.start=start
self.end=end
s=[tp(0,1),tp(0,3),tp(4,5)]
s.sort(key=lambda self: self.start)
print s
y=[ s[0] ]
for x in s[1:]:
if y[-1].end < x.start:
y.append(x)
elif y[-1].end == x.start:
y[-1].end = x.end
if x.end > y[-1].end:
y[-1].end = x.end
print y
Я также реализовал его для вычитания:
#subtraction
z=tp(1.5,5) #interval to be subtracted
s=[tp(0,1),tp(0,3), tp(3,4),tp(4,6)]
s.sort(key=lambda self: self.start)
print s
for x in s[:]:
if z.end < x.start:
break
elif z.start < x.start and z.end > x.start and z.end < x.end:
x.start=z.end
elif z.start < x.start and z.end > x.end:
s.remove(x)
elif z.start > x.start and z.end < x.end:
s.append(tp(x.start,z.start))
s.append(tp(z.end,x.end))
s.remove(x)
elif z.start > x.start and z.start < x.end and z.end > x.end:
x.end=z.start
elif z.start > x.end:
continue
print s
Оказывается, эта проблема была решена много раз - на разных уровнях фантазии, идя под номенклатурой (-ами): http://en.wikipedia.org/wiki/Interval_tree, http://en.wikipedia.org/wiki/Segment_tree, а также 'RangeTree'
(поскольку вопрос ОП включает в себя большое количество интервалов, которые имеют эти данные)
с точки зрения моего выбора выбора библиотеки python:
Из тестирования я нахожу, что большинство гвоздящих это с точки зрения полнофункционального и текущего python (не бит-гниения): классы "Interval" и "Union" из SymPy, см.: http://sympystats.wordpress.com/2012/03/30/simplifying-sets/
Еще один хороший выбор: более высокая производительность, но менее функциональная опция (например, не работала с удалением диапазона с плавающей запятой): https://pypi.python.org/pypi/Banyan
Наконец: выполните поиск вокруг самой SO, под любым из IntervalTree, SegmentTree, RangeTree, и вы найдете ответы/перехватчики еще больше.
Сортировка всех точек. Затем перейдите в список, увеличивая счетчик для "стартовых" точек и уменьшая его для "конечных" точек. Если счетчик достигает 0, то он действительно является конечной точкой одного из интервалов в объединении.
Счетчик никогда не будет отрицательным и достигнет 0 в конце списка.
Чтобы найти полное объединение интервалов в С++
#include <iostream>
#include <algorithm>
struct interval
{
int m_start;
int m_end;
};
int main()
{
interval arr[] = { { 9, 10 }, { 5, 9 }, { 3, 4 }, { 8, 11 } };
std::sort(
arr,
arr + sizeof(arr) / sizeof(interval),
[](const auto& i, const auto& j) { return i.m_start < j.m_start; });
int total = 0;
auto current = arr[0];
for (const auto& i : arr)
{
if (i.m_start >= current.m_end)
{
total += current.m_end - current.m_start;
current = i;
}
else if (i.m_end > current.m_end)
{
current.m_end = i.m_end;
}
}
total += current.m_end - current.m_start;
std::cout << total << std::endl;
}