Що ви опануєте
Ви навчитеся формулювати алгоритм через вхід, вихід і обмеження; представляти його словесно, псевдокодом та схемою; простежувати зміну стану й оцінювати кількість операцій. Задача — знайти першу доступну консультацію потрібної тривалості. Такий приклад поєднує послідовність, розгалуження та повторення.
Алгоритм і контракт
Алгоритм — скінченний опис визначених кроків для розв’язання класу задач. Вхід визначає початкові дані, вихід — результат. Контракт задає передумови й постумови. Для пошуку консультації вхід — впорядкований список інтервалів і потрібна тривалість; вихід — відповідний інтервал або повідомлення про відсутність. Передумова: початок кожного інтервалу менший за кінець, а тривалість додатна.
Стан — значення змінних на певному кроці. Інваріант — властивість, яка зберігається у визначених точках виконання. Під час пошуку всі вже переглянуті інтервали не підходять. Цей інваріант пояснює, чому повернення першого придатного інтервалу дає потрібний результат, якщо список має погоджений порядок.
Форми представлення
Словесний опис корисний для погодження правила: «переглядати інтервали за часом початку та повертати перший достатньої тривалості». Псевдокод використовує структуровані інструкції без прив’язки до конкретної мови. Блок-схема показує переходи між кроками. Код уточнює типи й операції виконуваного середовища. Всі форми повинні відповідати одному контракту.
ВХІД: intervals, required
ДЛЯ кожного (start, end) у intervals:
ЯКЩО end - start >= required:
ПОВЕРНУТИ (start, end)
ПОВЕРНУТИ відсутність результату
Час у цьому навчальному прикладі подано хвилинами від початку дня. Одиницю потрібно явно назвати, щоб не змішати години й хвилини. Для реальних календарних подій додатково враховують дату, часовий пояс і перетин із зайнятим часом. Псевдокод описує відбір із уже підготовлених вільних інтервалів.
Виконуваний приклад
def first_fit(intervals: list[tuple[int, int]], required: int):
if required <= 0:
raise ValueError("Тривалість має бути додатною")
for start, end in intervals:
if end <= start:
raise ValueError("Некоректний інтервал")
for start, end in intervals:
# Довжина інтервалу вимірюється у хвилинах.
if end - start >= required:
return (start, end)
return None
free = [(540, 550), (570, 600), (630, 640)]
assert first_fit(free, 20) == (570, 600)
assert first_fit([], 20) is None
print(first_fit(free, 20))
Кортеж tuple групує початок і кінець. Перший цикл перевіряє всі інтервали, щоб функція не приховала помилкові дані після раннього результату. Другий виконує пошук. return завершує функцію; None позначає відсутність відповідного інтервалу. Порядок у списку є частиною контракту, тому невпорядкований список може дати придатний результат, який не буде найранішим.
Таблиця трасування
Для required=20 перший інтервал має довжину 10, умова хибна. Другий має довжину 30, умова істинна: повертається (570, 600). Третій уже не переглядається під час пошуку. Таблиця трасування містить номер кроку, поточний інтервал, довжину, результат умови й дію. Вона допомагає пояснити виконання до запуску програми.
Перевірте інтервал рівно на 20 хвилин: знак >= повинен його прийняти. Якщо використати >, з’явиться помилка на границі. Для порожнього списку тіло циклу не виконується, тому функція переходить до return None. Для від’ємної тривалості виникає виняток ще до перевірки інтервалів.
Коректність і завершення
Коректність пояснюють через контракт та інваріант. До початку пошуку переглянутих елементів немає. Кожна невдала перевірка додає до них один інтервал, який не підходить. При успіху всі попередні не підходять, а поточний задовольняє умову. Коли елементи закінчилися, придатного інтервалу немає. Цикл завершується, оскільки перебирає скінченний список.
Для циклу while потрібно окремо знайти величину, що наближається до завершення: лічильник, кількість необроблених елементів або зменшуваний залишок. Умови з числами з плаваючою крапкою потребують уваги до точності. Відсутність зміни стану в тілі може спричинити нескінченне повторення, навіть якщо текст алгоритму здається логічним.
Складність
Часова складність описує зростання кількості операцій зі збільшенням входу. Тут перевірка даних і пошук виконують щонайбільше по одному проходу, тому маємо . Позначення приховує сталий множник і не вимірює секунди. Додаткова пам’ять для змінних дорівнює , якщо не враховувати вже переданий список.
Сортування перед пошуком може вимагати операцій і змінює загальну оцінку. Якщо вхід уже впорядкований за контрактом, повторне сортування зайве. Обґрунтування ефективності починається з характеру даних та очікуваного розміру. Для невеликих наборів ясність і перевірюваність можуть визначати вибір реалізації.
Типові помилки та практика
Поширені проблеми: неописаний порядок, змішані одиниці, раннє повернення без перевірки контракту, неправильний знак на границі та індекс поза списком. Виправлення починайте з конкретного контрприкладу. Якщо студент стверджує, що алгоритм працює, попросіть пояснити порожній вхід і значення на межі.
Самостійний аналіз
Змініть постановку: потрібно повернути всі придатні інтервали. Визначте новий тип результату, постумову й оцінку пам’яті. Поясніть, які перевірки залишаються, а які потребують змін. Для трьох власних наборів складіть таблицю трасування до запуску коду.
Інваріант і доказ завершення на прикладі пошуку
Інваріант циклу — твердження, яке зберігається перед кожною ітерацією. Для пошуку першого придатного інтервалу можна записати: усі вже переглянуті інтервали не задовольняють потрібну тривалість. На початку переглянутих інтервалів немає, отже твердження виконується. Якщо поточний інтервал закороткий, його пропуск зберігає інваріант. Якщо придатний — алгоритм повертає саме перший за заданим порядком.
Завершення доводимо скінченністю списку: на кожному кроці індекс збільшується, а кількість непереглянутих елементів зменшується. Коли список закінчився, усі інтервали перевірено; відсутність результату є коректною постумовою. Ці міркування пояснюють алгоритм незалежно від конкретної мови програмування.
Порядок входу важливий. Якщо потрібен найраніший час, список треба попередньо впорядкувати за початком або змінити алгоритм. Пошук першого в невпорядкованому списку відповідає іншому контракту. Сортування додає власні витрати й може змінити початкову послідовність; це слід зазначити в описі.
Порівняйте блок-схему, псевдокод і програму за одним тестом: два закороткі інтервали, потім придатний. На схемі простежте умови, у псевдокоді — зміну індексу, у програмі — фактичне повернення. Для порожнього списку шлях повинен одразу приводити до відсутності результату. Якщо представлення дають різні відповіді, уточніть контракт або виправте конкретний крок, зберігаючи зміст алгоритму.
Схема процесу
Фіксуємо одиниці, порядок і допустимі дані.
Самоперевірка
Як обґрунтувати відповідь своїми словами?
Підсумок
Контракт робить алгоритм перевірюваним. Псевдокод, схема, таблиця трасування й код описують різні аспекти одного правила. Інваріант пояснює коректність, а складність — зростання витрат зі збільшенням входу.