Тестовое задание
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

285 lines
11 KiB

3 years ago
tests = [
{
"data": {
"lesson": [1594663200, 1594666800],
"pupil": [
1594663340,
1594663389,
1594663390,
1594663395,
1594663396,
1594666472,
],
"tutor": [1594663290, 1594663430, 1594663443, 1594666473],
},
"answer": 3117,
},
{
"data": {
"lesson": [1594702800, 1594706400],
"pupil": [
1594702789,
1594704500,
1594702807,
1594704542,
1594704512,
1594704513,
1594704564,
1594705150,
1594704581,
1594704582,
1594704734,
1594705009,
1594705095,
1594705096,
1594705106,
1594706480,
1594705158,
1594705773,
1594705849,
1594706480,
1594706500,
1594706875,
1594706502,
1594706503,
1594706524,
1594706524,
1594706579,
1594706641,
],
"tutor": [
1594700035,
1594700364,
1594702749,
1594705148,
1594705149,
1594706463,
],
},
"answer": 3577,
},
{
"data": {
"lesson": [1594692000, 1594695600],
"pupil": [1594692033, 1594696347],
"tutor": [1594692017, 1594692066, 1594692068, 1594696341],
},
"answer": 3565,
},
]
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])
]
print(f"{small_intervals=}")
result = [i for i in intervals if i not in small_intervals]
print(f"{result=}")
print(f"{intervals=}")
return result
def extend_partial_included_intervals(interval_A, interval_B) -> list:
"""Если интервалы пересекаются, то мы заменяем их на сумму
перескающихся интервалов."""
# планирую поступать по тому же принципу что и выше: добавлять
# сумму в исходный список, итого получим список с пересечениями,
# который потом можно прогнать через поглатитель меньших
# интервалов
# если один из интервалов пустой, то пересечение будет пустым
if not interval_A or not interval_B:
return []
# если какой-то из инетрвалов имеет нулевую длину (начало=конец),
# то пересечение пустое
if interval_A[0] == interval_A[1] or interval_B[0] == interval_B[1]:
return []
# нужно проверить, что интервалы вообще пересекаются
# А и B не пересекаются, если
# начало А > конец B, или если начало B > конец А
if interval_A[0] > interval_B[1] or interval_B[0] > interval_A[1]:
return []
# сумма интервалов: наименьшее начало из А и Б, и наибольший конец
interval_begin = min(interval_A[0], interval_B[0])
interval_end = max(interval_A[1], interval_B[1])
print(f"get: {[interval_A, interval_B]}")
print(f"res: {[interval_begin, interval_end]}")
return [interval_begin, interval_end]
def extend_intervals(intervals) -> list:
extended_intervals = [
extend_partial_included_intervals(interval, intervals[k])
for i, interval in enumerate(intervals)
for k in range(i + 1, len(intervals))
if extend_partial_included_intervals(interval, intervals[k])
]
print(f"{extended_intervals=}")
uniq_extended_intervals = [i for i in extended_intervals if i not in intervals]
print(f"{uniq_extended_intervals=}")
result = intervals + uniq_extended_intervals
result = absorb_small_included_intervals(result)
return result
def find_inner_sum_of_intersections(interval_A, interval_B) -> list:
print(f"{interval_A=}")
print(f"{interval_B=}")
# проверить, может быт интервалы входят друг в друга, и тогда
# возвращаем больший
if interval_A[0] < interval_B[0] and interval_A[1] > interval_B[1]:
return interval_A
if interval_B[0] < interval_A[0] and interval_B[1] > interval_A[1]:
return interval_B
if interval_A[0] == interval_B[0] and interval_A[1] == interval_B[1]:
return []
# если один из интервалов пустой, то пересечение будет пустым
if not interval_A or not interval_B:
return []
# если какой-то из инетрвалов имеет нулевую длину (начало=конец),
# то пересечение пустое
if interval_A[0] == interval_A[1] or interval_B[0] == interval_B[1]:
return []
# нужно проверить, что интервалы вообще пересекаются
# А и B не пересекаются, если
# начало А > конец B, или если начало B > конец А
if interval_A[0] > interval_B[1] or interval_B[0] > interval_A[1]:
return []
# ищем сумму, а не пересечение
interval_begin = min(interval_A[0], interval_B[0])
interval_end = max(interval_A[1], interval_B[1])
return [interval_begin, interval_end]
3 years ago
def find_intersection(interval_A, interval_B) -> list:
# если один из интервалов пустой, то пересечение будет пустым
if not interval_A or not interval_B:
return []
# если какой-то из инетрвалов имеет нулевую длину (начало=конец),
# то пересечение пустое
if interval_A[0] == interval_A[1] or interval_B[0] == interval_B[1]:
return []
# нужно проверить, что интервалы вообще пересекаются
# А и B не пересекаются, если
# начало А > конец B, или если начало B > конец А
if interval_A[0] > interval_B[1] or interval_B[0] > interval_A[1]:
return []
# начало интервала = наименьший из начал интервалов А и B
# конец интервала это наименьший из концов интервалов А и B
interval_begin = max(interval_A[0], interval_B[0])
interval_end = min(interval_A[1], interval_B[1])
return [interval_begin, interval_end]
def enlarge_elapsed_intervals(intervals):
# ключ - начало, значние - конец. Если по ключу "начало" уже есть
# значение, то сохраняем max(существующее, новое)
# чистка конечных интервалов. ключ в словаре - начало интервала.
# проходим по исходному списку и выбираем наибольшее конечное
# значение (самый поздний) из интервалов с одинаковыми началами
beginnings = {interval[0]: interval[1] for interval in intervals}
for interval in intervals:
begin = interval[0]
end = interval[1]
beginnings[begin] = max(beginnings[begin], end)
print(f"{beginnings=}")
endings = {beginnings[k]: k for k in beginnings}
for interval in intervals:
begin = interval[0]
end = interval[1]
if endings.get(end):
endings[end] = min(endings[end], begin)
print(f"{endings=}")
result = [[endings[k], k] for k in endings]
return result
3 years ago
def appearance(intervals):
lesson = intervals["lesson"]
tutor = [
intervals["tutor"][i : i + 2] for i in range(0, len(intervals["tutor"]), 2)
]
pupil = [
intervals["pupil"][i : i + 2] for i in range(0, len(intervals["pupil"]), 2)
]
pupil = extend_intervals(pupil)
print(f"!!!{pupil=}")
tutor = enlarge_elapsed_intervals(tutor)
3 years ago
# получаем все интервалы студента на уроке
pupil_on_lesson = [
find_intersection(lesson, p) for p in pupil if find_intersection(lesson, p)
3 years ago
]
print(f"{pupil_on_lesson=}")
# по очереди сравниваем каждый из интервалов преподавателя с
# каждым из интервалов пересечения студента на уроке
tutor_on_pupil = [
find_intersection(pol, t) for t in tutor for pol in pupil_on_lesson
3 years ago
]
print(f"{tutor_on_pupil=}")
# создаем список из продолжительности каждого интервала, если он
# не пустой, а затем суммируем все элементы
answer = sum([intr[1] - intr[0] for intr in tutor_on_pupil if intr])
print("----------------------------------------")
return answer
if __name__ == "__main__":
for i, test in enumerate(tests):
test_answer = appearance(test["data"])
assert (
test_answer == test["answer"]
), f'Error on test case {i}, got {test_answer}, expected {test["answer"]}'