Три рівні. Кожен наступний спирається на попередній, але братися можна за будь-який.
Пиши в окремому файлі homework_09.py або в новому зошиті — головне, щоб код
запускався й друкував результат.
Дозволено з бібліотек: sys, time. Більше нічого не треба.
Циклів і умов ми ще не проходили — усі завдання розв'язуються без них, окрім
позначених 🔁 (там цикл і if дозволені як забігання наперед у теми 11–12).
Одне правило на всі рівні. Множина порядку не зберігає, а хеш рядка змінюється
між запусками. Тому будь-який вивід множини рядків друкуй через sorted(...), а
в assert-ах порівнюй множини з множинами (== дивиться на склад, не на порядок),
а не списки зі списками.
рівень 1Рівень 1 — База
1.1 · Скільки різних
Склади список щонайменше з 12 елементів, у якому є повтори, — наприклад, назви фільмів, які ти дивився за місяць, або міста, де ти був. Далі:
- надрукуй довжину списку;
- зроби з нього множину й надрукуй її довжину;
- надрукуй, скільки повторів було відкинуто;
- надрукуй сам набір різних значень через
sorted(...).
Зроблено, якщо: у виводі видно всі чотири рядки, а наприкінці виконуються
assert len(унікальні) < len(мій_список)
assert len(мій_список) - len(унікальні) == кількість_повторів
і жодного разу не з'явився {} там, де мала бути порожня множина.
1.2 · Дві помилки, які треба побачити
Напиши три короткі перевірки й надрукуй результат кожної:
type({})іtype(set())— покажи, що це різні типи;discardна елементі, якого немає, — покажиassert-ом, що множина не змінилась;removeна тому самому елементі — злови помилку черезtry/except KeyErrorі надрукуй її текст.
Зроблено, якщо: програма доходить до кінця й не падає, але у виводі видно
справжній KeyError та його елемент. Підказка: конструкція
except KeyError as помилка: print(помилка) робить рівно це.
рівень 2Рівень 2 — Плюс
2.1 · Два дні відвідувань
Дано сирі логи (у них є повтори — це нормально, людина заходила двічі):
вчора = ["/головна", "/ціни", "/про-нас", "/головна", "/ціни"]
сьогодні = ["/ціни", "/контакти", "/головна", "/контакти"]
Порахуй і надрукуй чотири відповіді, кожну одним виразом над множинами:
- які сторінки дивились обидва дні;
- які сторінки з'явились сьогодні;
- які сторінки зникли;
- які сторінки дивились лише в один із двох днів.
Зроблено, якщо: виконуються всі чотири перевірки
assert спільні == {"/головна", "/ціни"}
assert зʼявились == {"/контакти"}
assert зникли == {"/про-нас"}
assert лише_в_одному == {"/контакти", "/про-нас"}
і ти пояснив у коментарі одним реченням, чому пункт 4 — це не сума пунктів 2 і 3 випадково, а завжди.
2.2 · Права доступу без жодного if
Дано:
потрібні = {"читати", "писати"}
адмін = {"читати", "писати", "видаляти", "запрошувати"}
гість = {"читати"}
редактор = {"читати", "писати"}
Для кожного з трьох користувачів надрукуй один рядок виду
адмін: доступ дозволено = True. Використай тільки порівняння множин
(<=, >=, <, >), без жодної перевірки if і без len.
Далі відповідь на питання зі зіркою: покажи кодом, що редактор — підмножина
потрібні і надмножина потрібні одночасно, і поясни в коментарі, що це означає.
Зроблено, якщо: виконуються
assert потрібні <= адмін
assert not (потрібні <= гість)
assert редактор <= потрібні and редактор >= потрібні
assert редактор == потрібні
і в коментарі сказано, чому останні два рядки — це одне й те саме твердження.
2.3 · 🔁 Скільки коштує «немає»
Побудуй список із 50 000 рядків виду "слово00000", "слово00001", … і множину
з тих самих рядків. Заміряй time.perf_counter() навколо перевірки
"такого-слова-немає" in ... для обох структур, повторивши перевірку 100 разів.
Зроблено, якщо: ти надрукував обидва часи й відношення між ними, а в коментарі відповів на два питання:
- чому ми міряємо саме випадок «слова немає», а не «слово є першим»;
- чому в лекції ті самі дані показані в кроках, а не в мілісекундах.
рівень 3Рівень 3 — Виклик
3.1 · Своя алгебра множин
Реалізуй чотири функції, які повторюють вбудовані операції, не використовуючи
операторів |, &, -, ^ і однойменних методів. Дозволено тільки in, not in,
add, set() і цикл for:
моє_обʼєднання(a, b)мій_перетин(a, b)моя_різниця(a, b)моя_симетрична(a, b)
Зроблено, якщо: усі перевірки проходять на трьох різних парах множин — таких, що перетинаються частково, не перетинаються зовсім і збігаються повністю:
for A, B in [({1,2,3}, {3,4}), ({1,2}, {3,4}), ({1,2}, {1,2})]:
assert моє_обʼєднання(A, B) == A | B
assert мій_перетин(A, B) == A & B
assert моя_різниця(A, B) == A - B
assert моя_симетрична(A, B) == A ^ B
print("✅ уся алгебра збіглася")
і ти дописав у коментарі, скільки разів кожна функція виконує in залежно від
розмірів a і b.
3.2 · Множина множин
Є список команд, кожна — список імен. Ті самі люди могли записатись у різному порядку:
команди = [["Аня", "Богдан"], ["Богдан", "Аня"], ["Галя", "Оля", "Петро"], ["Аня", "Галя"]]
Порахуй, скільки серед них різних команд. Пряме set(команди) не спрацює —
розберись, чому саме, і полагодь це одним словом.
Далі зроби словник «команда → кількість людей у ній» і надрукуй його.
Зроблено, якщо: виконуються
assert len(різні_команди) == 3
assert frozenset({"Богдан", "Аня"}) in різні_команди
assert розміри[frozenset({"Галя", "Оля", "Петро"})] == 3
і ти пояснив у коментарі двома реченнями, чому тут потрібен саме frozenset,
а не кортеж: що зламалось би, якби ключем був ("Аня", "Богдан").
3.3 · Де межа заповнення
Спираючись на розділ 15 практики, знайди експериментом, на якому за рахунком елементі
множина перебудовує таблицю. Зроби те саме зі словником (додавай пари номер: номер)
і надрукуй обидві послідовності «номер елемента → новий sys.getsizeof».
Зроблено, якщо: ти показав числами, що множина перебудовується раніше за словник на тому самому розмірі, і письмово відповів, чому так: у словника дірки живуть у маленькій індексній таблиці по байту на комірку, а в множини кожна дірка — повноцінна комірка. Одного речення досить, але воно має спиратись на твої числа.
Підказки
{}— це словник. Якщо десь у розв'язку з'явився порожній набір, він мусить бутиset(). Найшвидший спосіб зловити помилку — надрукуватиtype(...).- Порівнюй множини з множинами.
sorted(A) == sorted(B)теж працює, але ховає саму ідею:A == Bдля множин дивиться на склад, а не на порядок, і це коротше. - У 2.2 не потрібен жоден
if.потрібні <= адмінуже повертаєTrueабоFalse— залишається це надрукувати. - У 3.1 симетричну різницю простіше зібрати з двох різниць, які ти вже написав, ніж окремим проходом. Але спершу перевір, чи не порушує це умову задачі.
- У 3.2 підказка в одне слово —
frozenset. Друга частина питання складніша: подумай, скільки різних кортежів дадуть ті самі двоє людей і чому це псує підрахунок. - У 3.3 не роби висновку з одного стрибка. Візьми хоча б 40 елементів і подивись на всі стрибки: перший може здатися випадковим, три поспіль — уже закономірність.