Дмитрий
3 years ago
6 changed files with 279 additions and 141 deletions
@ -1,105 +0,0 @@
|
||||
"""Модуль, содержащий функции для чистки интервалов. |
||||
Под 'чисткой' подразумевается: |
||||
- удаление меньших интервалов, полностью входящих в большие (поглощение) |
||||
- расширение пересекающихся интервалов (сумма интервалов) |
||||
""" |
||||
|
||||
|
||||
def get_smaller_interval(interval_A, interval_B) -> list: |
||||
"""Если один из интервалов включает в себя другой, возвращаем |
||||
меньший. Если интервалы не входят друг в друга, возвращаем пустой |
||||
интервал.""" |
||||
if interval_A[0] <= interval_B[0] and interval_A[1] >= interval_B[1]: |
||||
return interval_B |
||||
|
||||
if interval_B[0] <= interval_A[0] and interval_B[1] >= interval_A[1]: |
||||
return interval_A |
||||
|
||||
return [] |
||||
|
||||
|
||||
def absorb_small_included_intervals(intervals: list) -> list: |
||||
"""Возвращает список интервалов без вложенных интервалов. |
||||
|
||||
Вложенный интервал - интервал меньшего размера, полностью входящий |
||||
в больший. |
||||
|
||||
Стоит учитывать, что функция работает только если интервалы |
||||
совпадают по началу или концу, если интервалы входят друг в друга |
||||
частично (пересекаются) -- здесь мы их не обрабатываем. |
||||
|
||||
""" |
||||
# формируем список из меньших интервалов, входящих в бОльшие |
||||
small_intervals = [ |
||||
get_smaller_interval(interval, intervals[k]) |
||||
for i, interval in enumerate(intervals) |
||||
for k in range(i + 1, len(intervals)) |
||||
if get_smaller_interval(interval, intervals[k]) |
||||
] |
||||
|
||||
# в качестве результа у нас будет новый список, состоящий из |
||||
# принятого списка интервалов `intervals`, за исключением входящих меньших |
||||
# интервалов `small_intervals` |
||||
result = [i for i in intervals if i not in small_intervals] |
||||
|
||||
return result |
||||
|
||||
|
||||
def get_union_of_partly_intersecting_intervals(interval_A, interval_B) -> list: |
||||
"""Если интервалы A и B пересекаются, то возвращаем новый |
||||
интервал, включающий A и B.""" |
||||
|
||||
# если один из интервалов пустой, то пересечение будет пустым |
||||
if not interval_A or not interval_B: |
||||
return [] |
||||
|
||||
# если какой-то из интервалов имеет нулевую длину (начало=конец), |
||||
# то пересечение пустое |
||||
if interval_A[0] == interval_A[1] or interval_B[0] == interval_B[1]: |
||||
return [] |
||||
|
||||
# нужно проверить, что интервалы вообще пересекаются |
||||
# A и B не пересекаются, если |
||||
# начало A > конец B, или если начало B > конец A |
||||
if interval_A[0] > interval_B[1] or interval_B[0] > interval_A[1]: |
||||
return [] |
||||
|
||||
# сумма интервалов: берем наименьшее начало из A и B, и наибольший конец |
||||
# из A и B |
||||
interval_begin = min(interval_A[0], interval_B[0]) |
||||
interval_end = max(interval_A[1], interval_B[1]) |
||||
return [interval_begin, interval_end] |
||||
|
||||
|
||||
def get_extended_intervals(intervals) -> list: |
||||
"""Возвращает расширенные интервалы из списка интервалов. |
||||
|
||||
Расширенный интервал -- это интервал, который есть сумма двух |
||||
пересекающихся интервалов.""" |
||||
extended_intervals = [ |
||||
get_union_of_partly_intersecting_intervals(interval, intervals[k]) |
||||
for i, interval in enumerate(intervals) |
||||
for k in range(i + 1, len(intervals)) |
||||
if get_union_of_partly_intersecting_intervals(interval, intervals[k]) |
||||
] |
||||
|
||||
# список extended_intervals может содержать дублирующиеся |
||||
# интервалы из списка intervals, поэтому мы возвращаем только |
||||
# отсутствующие в intervals значения |
||||
uniq_extended_intervals = [i for i in extended_intervals if i not in intervals] |
||||
return uniq_extended_intervals |
||||
|
||||
|
||||
def normilize_intervals(intervals) -> list: |
||||
"""Разбивает полученный список на пары, расширяет интервалы, |
||||
убирает вложенные интервалы.""" |
||||
|
||||
# из списка [1,2,3,4] формируем [[1,2], [3,4]] |
||||
intervals_ = [intervals[i : i + 2] for i in range(0, len(intervals), 2)] |
||||
|
||||
# получаем расширенные интервалы |
||||
extended_intervals = get_extended_intervals(intervals_) |
||||
|
||||
# убираем вложенные интервалы |
||||
n_intervals = absorb_small_included_intervals(intervals_ + extended_intervals) |
||||
return n_intervals |
@ -0,0 +1,67 @@
|
||||
"""Модуль, содержащий функции для чистки интервалов. |
||||
Под 'чисткой' подразумевается: |
||||
- удаление меньших интервалов, полностью входящих в большие (поглощение) |
||||
- расширение пересекающихся интервалов (сумма интервалов) |
||||
""" |
||||
|
||||
|
||||
def absorb_small_included_intervals(intervals: list) -> list: |
||||
"""Возвращает список интервалов без вложенных интервалов. |
||||
|
||||
Вложенный интервал - интервал меньшего размера, полностью входящий |
||||
в больший. |
||||
|
||||
Стоит учитывать, что функция работает только если интервалы |
||||
совпадают по началу или концу, если интервалы входят друг в друга |
||||
частично (пересекаются) -- здесь мы их не обрабатываем. |
||||
|
||||
""" |
||||
# запоминаем самый перевый интервал, раньше него интервалов не |
||||
# будет. Если текущий интервал входит в последний добавленный |
||||
# нами, т.е. он заканчивается раньше, чем тот который мы добавили, |
||||
# то мы его не добавляем в результат, так как он вложенный |
||||
big_intervals = [intervals[0]] |
||||
for i in range(1, len(intervals)): |
||||
if big_intervals[-1][1] >= intervals[i][1]: |
||||
continue |
||||
else: |
||||
big_intervals.append(intervals[i]) |
||||
|
||||
return big_intervals |
||||
|
||||
|
||||
def merge_intersected_intervals(intervals) -> list: |
||||
"""Возвращает расширенные интервалы. |
||||
|
||||
Расширенные интервалы -- это интервалы, состоящие из суммы |
||||
частично пересекающихся интервалов.""" |
||||
|
||||
# запоминаем самый первый интервал. Проходимся по списку, и если |
||||
# начало текущего интервала меньше, чем конец последнего |
||||
# добавленного, то мы расширяем записанный интервал до конца |
||||
# текущего. В противном случае просто добавляем текущий интервал в |
||||
# итоговый список интервалов. |
||||
res = [intervals[0]] |
||||
for i in range(1, len(intervals)): |
||||
if res[-1][1] >= intervals[i][0]: |
||||
res[-1][1] = max(intervals[i][1], res[-1][1]) |
||||
else: |
||||
res.append(intervals[i]) |
||||
|
||||
return res |
||||
|
||||
|
||||
def normilize_intervals(intervals) -> list: |
||||
"""Разбивает полученный список на пары, расширяет интервалы, |
||||
убирает вложенные интервалы.""" |
||||
|
||||
# из списка [1,2,3,4] формируем [[1,2], [3,4]] |
||||
intervals_ = [intervals[i : i + 2] for i in range(0, len(intervals), 2)] |
||||
|
||||
# убираем вложенные интервалы |
||||
n_intervals = absorb_small_included_intervals(intervals_) |
||||
|
||||
# расширяем пересекающиеся интервалы |
||||
result = merge_intersected_intervals(n_intervals) |
||||
|
||||
return result |
@ -0,0 +1,48 @@
|
||||
"""Модуль поиска пересечений интервалов.""" |
||||
|
||||
|
||||
def has_intersection(interval_A, interval_B) -> bool: |
||||
"""Пересекаются ли интервалы.""" |
||||
if interval_A[1] <= interval_B[0]: |
||||
return False |
||||
|
||||
if interval_B[1] <= interval_A[0]: |
||||
return False |
||||
|
||||
return True |
||||
|
||||
|
||||
def get_intersection(interval_A, interval_B) -> list: |
||||
"""Возвращает пересечение двух интервалов, если они не |
||||
пересекаются, возвращает пустой интервал.""" |
||||
if not has_intersection(interval_A, interval_B): |
||||
return [] |
||||
|
||||
begin = max(interval_A[0], interval_B[0]) |
||||
end = min(interval_A[1], interval_B[1]) |
||||
|
||||
return [begin, end] |
||||
|
||||
|
||||
def get_all_intersections(intervals_A, intervals_B) -> list: |
||||
"""Принимает два списка интервалов, для которых нужно найти |
||||
пересечения. Возвращает список найденных пересечений.""" |
||||
|
||||
# задаем индексы для каждого из интервалов |
||||
p_A, p_B = 0, 0 |
||||
res = [] |
||||
|
||||
# Если текущий интервал в списке intervals_A заканчивается позже, |
||||
# чем текущий интервал в списке intervals_B, нам нужно перейти на |
||||
# следующий интервал в списке intervals_B. Таким образом по |
||||
# спискам мы пройдем всего лишь раз. |
||||
while p_A < len(intervals_A) and p_B < len(intervals_B): |
||||
A, B = intervals_A[p_A], intervals_B[p_B] |
||||
if has_intersection(A, B): |
||||
res.append(get_intersection(A, B)) |
||||
|
||||
if A[1] <= B[1]: |
||||
p_A += 1 |
||||
else: |
||||
p_B += 1 |
||||
return res |
@ -0,0 +1,158 @@
|
||||
from task_3 import has_intersection, get_intersection |
||||
|
||||
|
||||
def test_case_1(): |
||||
test = [ |
||||
[1594705106, 1594706480], |
||||
[1594705158, 1594705773], |
||||
[1594705849, 1594706480], |
||||
] |
||||
result = [ |
||||
[1594705106, 1594706480], |
||||
[1594705158, 1594705773], |
||||
] |
||||
# print(f"{test=}") |
||||
|
||||
answer = enlarge_elapsed_intervals(test) |
||||
assert answer == result, f"Error in test_case_1:\n{answer} \n {result}" |
||||
|
||||
|
||||
def test_case_2(): |
||||
test = [ |
||||
[1594705106, 1594706480], |
||||
[1594705106, 1594706490], |
||||
[1594705849, 1594706495], |
||||
] |
||||
result = [ |
||||
[1594705106, 1594706490], |
||||
[1594705849, 1594706495], |
||||
] |
||||
# print(f"{test=}") |
||||
|
||||
answer = enlarge_elapsed_intervals(test) |
||||
assert answer == result, f"Error in test_case_1:\n{answer}\n {result}" |
||||
|
||||
|
||||
def test_case_3(): |
||||
test = [ |
||||
[21, 30], |
||||
[25, 32], |
||||
[33, 40], |
||||
[33, 35], |
||||
[41, 45], |
||||
[42, 43], |
||||
[44, 45], |
||||
[46, 50], |
||||
[47, 51], |
||||
] |
||||
result = [ |
||||
[21, 32], |
||||
[33, 40], |
||||
[41, 45], |
||||
[46, 51], |
||||
] |
||||
|
||||
answer = [] |
||||
for i in range(len(test)): |
||||
for k in range(i, len(test)): |
||||
a = find_inner_sum_of_intersections(test[i], test[k]) |
||||
answer.append(a) |
||||
print(f"{answer=}") |
||||
answer = [a for a in answer if a] |
||||
test.extend(answer) |
||||
print("!___") |
||||
print(test) |
||||
answer = enlarge_elapsed_intervals(test) |
||||
|
||||
print(f"{answer=}") |
||||
assert answer == result, f"Error in test_case_1:\n{answer}\n {result}" |
||||
|
||||
|
||||
def test_case_4(): |
||||
test = [ |
||||
[21, 30], |
||||
[33, 40], |
||||
[33, 35], # |
||||
[41, 45], |
||||
[42, 43], # |
||||
[44, 45], # |
||||
] |
||||
result = [ |
||||
[21, 30], |
||||
[33, 40], |
||||
[41, 45], |
||||
] |
||||
|
||||
answer = absorb_small_included_intervals(test) |
||||
assert answer == result, f"Error in test_case_4:\n{answer}\n {result}" |
||||
|
||||
|
||||
def test_case_5(): |
||||
test = [ |
||||
[21, 30], # |
||||
[28, 40], # |
||||
[33, 35], |
||||
[41, 45], |
||||
[42, 43], |
||||
[44, 45], |
||||
[53, 60], |
||||
[51, 55], |
||||
] |
||||
result = [ |
||||
[21, 40], |
||||
[41, 45], |
||||
[51, 60], |
||||
] |
||||
|
||||
answer = get_extended_intervals(test) |
||||
assert answer == result, f"Error in extend_intervals:\n{answer}\n {result}" |
||||
|
||||
|
||||
def absorb(): |
||||
test = [ |
||||
[21, 30], |
||||
[33, 40], |
||||
[33, 35], # |
||||
[41, 45], |
||||
[42, 43], # |
||||
[44, 45], # |
||||
] |
||||
result = [ |
||||
[21, 30], |
||||
[33, 40], |
||||
[41, 45], |
||||
] |
||||
|
||||
test_b = {i[0]: i[1] for i in test} |
||||
beg = [test[k] for k, v in enumerate(test_b) if test[k][1] < test_b[v]] |
||||
|
||||
test_e = {i[1]: i[0] for i in test} |
||||
end = [test[k] for k, v in enumerate(test_e) if test[k][0] > test_e[v]] |
||||
|
||||
print(beg, end) |
||||
|
||||
|
||||
def inters(): |
||||
print(has_intersection([21, 25], [22, 30])) |
||||
print(has_intersection([22, 30], [21, 25])) |
||||
print(has_intersection([21, 25], [26, 30])) |
||||
print(has_intersection([26, 30], [21, 25])) |
||||
|
||||
|
||||
def inters2(): |
||||
print(get_intersection([21, 25], [22, 30])) |
||||
print(get_intersection([22, 30], [21, 25])) |
||||
print(get_intersection([22, 30], [31, 33])) |
||||
|
||||
|
||||
if __name__ == "__main__": |
||||
|
||||
# test_case_1() |
||||
# test_case_2() |
||||
# test_case_3() |
||||
# test_case_4() |
||||
# test_case_5()\ |
||||
# absorb() |
||||
|
||||
# inters() |
||||
inters2() |
Loading…
Reference in new issue