План v2 по диагностике (квиз 14/32) + 4 новые задачи (hash, heap, dijkstra, subnet) + тренажёр 60 карточек
This commit is contained in:
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
diag/solutions/
|
diag/solutions/
|
||||||
diag/submissions/
|
diag/submissions/
|
||||||
diag/dist/
|
diag/dist/
|
||||||
diag/tasks/*/t_0*
|
diag/tasks/*/t_*
|
||||||
diag/tasks/07_epoll/solution
|
diag/tasks/07_epoll/solution
|
||||||
diag/tasks/09_gdb/crash_checked
|
diag/tasks/09_gdb/crash_checked
|
||||||
diag/tasks/08_bash/big.log
|
diag/tasks/08_bash/big.log
|
||||||
|
|||||||
@@ -1,40 +1,52 @@
|
|||||||
# ИУП: подготовка к собеседованию Eltex (C/C++, алгоритмы, Linux, сети)
|
# ИУП: подготовка к собеседованию Eltex (C/C++, алгоритмы, Linux, сети)
|
||||||
|
|
||||||
Личный учебный план и диагностический пакет. Репозиторий обновляется по ходу недели.
|
Личный учебный план и пакет задач с автопроверкой. Обновляется по ходу недели.
|
||||||
|
|
||||||
## Что внутри
|
## Что внутри
|
||||||
|
|
||||||
- `WEEK7.md` — **рабочий документ**: интенсив на 7 дней (D0 23.09 → D7 30.09), три блока
|
- `WEEK7.md` — **рабочий документ**: интенсив на 7 дней (D0 23.09 → D7 30.09), пересобранный
|
||||||
в дне (алгоритмы / системное / практика+разбор), у каждого дня контрольная точка.
|
по результату диагностики (квиз 14/32: алгоритмы 1/8, Linux 3/8, сети 3/8, C/C++ 7/8).
|
||||||
|
Каждый день: карточки 30 мин → алгоритмы 3 ч → системное 3 ч → разбор и код 1,5 ч.
|
||||||
- `PLAN.md` — полный трек M1–M10 на пост-офферный период (модули, артефакты, материалы).
|
- `PLAN.md` — полный трек M1–M10 на пост-офферный период (модули, артефакты, материалы).
|
||||||
- `CONTEXT.md` — вводные, разбор вакансии (Eltex, hh #136775547), статус лейна.
|
- `TRACKER.md` — дневник: что решено, где встал, сколько минут. Это вход для калибровки.
|
||||||
- `diag/` — диагностический пакет:
|
- `diag/` — пакет:
|
||||||
- `quiz.py` + `quiz.json` — 32 вопроса по 4 доменам (C/C++, алгоритмы, Linux, сети);
|
- `quiz.py` + `quiz.json` — 32 вопроса по 4 доменам;
|
||||||
- `tasks/01..09` — 9 практических задач с автопроверкой (ASAN/UBSAN, python-драйверы);
|
- `tasks/01..09` — диагностика: биты, список, ring buffer, IPv4-заголовок, НВП, потоки,
|
||||||
- `grade.py` — прогон задач с автооценкой;
|
epoll-сервер, bash по логу, разбор падения в gdb;
|
||||||
- `questions_export.txt` — вопросы квиза текстом (можно отвечать строкой в чате).
|
- `tasks/10..13` — добор под слабые домены: хеш-таблица с открытой адресацией, куча
|
||||||
- `TRACKER.md` — дневник: что решено, где встал, сколько минут.
|
(`heapify` O(n), top-K), Дейкстра, арифметика подсетей;
|
||||||
|
- `facts_drill.py` + `facts.json` — 60 карточек «числа и факты» (сложности, байты
|
||||||
|
заголовков, `/26 = 62`, TTL, порядок TCP-хендшейка, паддинг структур);
|
||||||
|
- `grade.py` — автопроверка задач (ASAN/UBSAN, таймауты, PASS/FAIL + полный лог);
|
||||||
|
- `questions_export.txt` — вопросы квиза текстом.
|
||||||
|
|
||||||
## Как начать
|
## Как начать
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://git.kodlo.art/Kodlo/eduplan-cpp-eltex.git
|
git clone https://git.kodlo.art/Kodlo/eduplan-cpp-eltex.git
|
||||||
cd eduplan-cpp-eltex/diag
|
cd eduplan-cpp-eltex/diag
|
||||||
python3 quiz.py # квиз, ответы — номер варианта
|
python3 quiz.py # квиз (или --answers "1-3 2-4 ...")
|
||||||
python3 grade.py # все 9 задач (с санитайзерами)
|
python3 grade.py # все 13 задач с санитайзерами
|
||||||
python3 grade.py 01 04 # выборочно
|
python3 grade.py 10 11 13 # выборочно
|
||||||
|
python3 grade.py --fast # без санитайзеров
|
||||||
|
python3 facts_drill.py # 20 карточек интерактивно
|
||||||
|
python3 facts_drill.py --all # все 60
|
||||||
```
|
```
|
||||||
|
|
||||||
Нужны `g++` (C++17), `gcc`, `python3`; для задачи 07 — только Linux-сокеты,
|
Нужны `g++` (C++17), `gcc`, `python3`; для `07_epoll` — Linux-сокеты, для `08_bash` —
|
||||||
для 08 — bash/awk/sort. Никаких внешних зависимостей.
|
bash/awk/sort. Внешних зависимостей нет. На Windows задачи 07 и 08 удобнее гонять в WSL.
|
||||||
|
|
||||||
## Правила честности
|
## Правила честности
|
||||||
|
|
||||||
Эталонных решений в репозитории нет — они лежат только на машинке студии, чтобы
|
Эталонных решений в репозитории нет (лежат только на машинке студии) — иначе автопроверка
|
||||||
автопроверка не протухла. Задача 09 (`answer.txt`) — часть оценки: важен ход разбора.
|
превращается в шпаргалку. Требования по сложности, которые тест не ловит (например
|
||||||
|
`heapify` за O(n)), проверяются на разборе по коду. `answer.txt` в задаче 09 — обязательная
|
||||||
|
часть оценки.
|
||||||
|
|
||||||
## Как отдавать результаты
|
## Куда отдавать результаты
|
||||||
|
|
||||||
- `python3 grade.py --submit` — складывает `results_*.json` и свои файлы решений рядом;
|
- ветка `answers/<день>` в этом репозитории;
|
||||||
- либо коммит в ветку `answers/<день>` и push (доступ на запись у аккаунта `tura`);
|
- либо файлы/`results_*.json` в чат;
|
||||||
- либо файлами в чат / в топик лейна.
|
- либо `python3 grade.py --submit` — складывает результаты и решения рядом.
|
||||||
|
|
||||||
|
Ежедневный блок плана приходит в топик лейна в 09:00 (НСК).
|
||||||
|
|||||||
@@ -1,138 +1,167 @@
|
|||||||
# ИУП — интенсив 7 дней (фуллтайм, собеседование ≤ 1 неделя)
|
# ИУП — интенсив 7 дней (фуллтайм, собеседование ≤ 1 неделя)
|
||||||
|
|
||||||
Срок: собеседование максимум через неделю, подготовка — фуллтайм (~8 ч/день).
|
**Версия 2, 23.09** — пересобрана по результату диагностики (квиз 14/32).
|
||||||
Цель интенсива: закрыть ровно то, что спросят на входе в Eltex (C/C++, базовые алгоритмы и
|
Срок: собеседование максимум через неделю, подготовка фуллтайм (~8 ч/день).
|
||||||
структуры данных, Linux, L2/L3-сети, инструменты), и не утонуть в глубине.
|
Цель: закрыть ровно то, что спросят на входе в Eltex (C/C++, базовые алгоритмы и структуры
|
||||||
|
данных, Linux, L2/L3-сети, инструменты), и не утонуть в глубине.
|
||||||
|
|
||||||
|
## Что показала диагностика (это и определило план)
|
||||||
|
|
||||||
|
| Домен | Результат | Вывод |
|
||||||
|
|---|---|---|
|
||||||
|
| C/C++ | 7/8 | сильная сторона, **времени почти не даём** |
|
||||||
|
| Алгоритмы | **1/8** | провал: формальные свойства (сложности, условия применимости) |
|
||||||
|
| Linux | 3/8 | пропущены fork/COW, epoll, mmap, waitpid — «не был уверен» |
|
||||||
|
| Сети | 3/8 | пропущены числа и байты (14/4/62/TTL), порядок TCP-хендшейка |
|
||||||
|
|
||||||
|
Отдельно: из 32 вопросов **11 пропущены**, и это почти все «точные факты» — числа, байты,
|
||||||
|
сложности. То есть база есть, а не хватает заученных формулировок и уверенности в них.
|
||||||
|
Поэтому в плане появился ежедневный блок карточек (`facts_drill.py`), а C++ урезан:
|
||||||
|
за него отвечает сильная сторона, забирать у неё часы смысла нет.
|
||||||
|
|
||||||
## Правила интенсива
|
## Правила интенсива
|
||||||
|
|
||||||
1. Каждый день — три блока: **утро (3 ч) алгоритмы**, **день (3 ч) системное/язык**,
|
1. Каждый день — три блока: **карточки (30 мин) → алгоритмы (3 ч) → системное (3 ч) →
|
||||||
**вечер (2 ч) практика + разбор**. Блок не начинается, пока не закрыта вчерашняя точка.
|
разбор и код (1,5 ч)**. Блок не начинается, пока не закрыта вчерашняя точка.
|
||||||
2. День закрыт, только если: решены задачи дня **без подсказки**, написан/починен код дня,
|
2. День закрыт, только если: решены задачи дня **без подсказки**, написан/починен код дня,
|
||||||
сделана **одна задача вслух на таймер** (10 мин: 2 мин на разбор + код + сложность).
|
сделана **одна задача вслух на таймер** (10 мин: разбор + код + сложность), заполнен
|
||||||
3. Всё пишем, а не читаем: теория — только под конкретную задачу, максимум 40 минут.
|
трекер.
|
||||||
4. Трекер: `/home/pi/eduplan/diag/submissions/` или файл у себя — что решено, где встал,
|
3. Теория — только под конкретную задачу, максимум 40 минут. Всё остальное — код и разбор
|
||||||
сколько минут. Это вход для калибровки на следующий день.
|
своих ошибок по трекеру.
|
||||||
5. Что осознанно НЕ лезет в неделю: полноценное ядро (модуль+драйвер), электроника,
|
4. Требования по сложности, которые тест не ловит (например `heapify` за O(n)) — проверяю
|
||||||
Docker-продакшн. Ядро — обзорно на D6, электроника — после оффера (см. `PLAN.md`).
|
глазами по коду при разборе. Если делаешь «на оценку» не то, что заявлено, увижу.
|
||||||
6. Если день провален — не догоняем всё, а переносим хвост в D7 (день повтора) и режем
|
5. Что осознанно НЕ лезет в неделю: полноценное ядро (модуль+драйвер как продукт),
|
||||||
второстепенное (M8-обзор, docker).
|
электроника и схемотехника, Docker в продакшне. Ядро — обзорно на D6, электроника —
|
||||||
|
после оффера (полный трек `PLAN.md`, модули M8–M9).
|
||||||
|
6. Провалил день — не догоняю всё, переношу хвост в D7 и режу второстепенное (ядро, docker).
|
||||||
|
|
||||||
Материалы: cppreference (C и C++), man7.org (разделы 2/3/7), LKMPG, docs.kernel.org,
|
Материалы: cppreference (C и C++), man7.org (разделы 2/3/7), Beej's Guide to Network
|
||||||
OSTEP (главы про процессы/память), Beej's Guide to Network Programming, RFC 791/793,
|
Programming, RFC 791/793, Wireshark wiki, OSTEP (процессы/память), LKMPG, docs.kernel.org.
|
||||||
Wireshark wiki. Задачи: Codeforces с фильтром по тегам и рейтингу 800–1400
|
Задачи: Codeforces с фильтром по тегу и рейтингу 800–1400
|
||||||
(`https://codeforces.com/problemset?tags=two+pointers,800-1400` — менять тег),
|
(`https://codeforces.com/problemset?tags=two+pointers,800-1400` — тег менять),
|
||||||
Яндекс Тренировки по алгоритмам (бесплатные контесты с разборами,
|
Яндекс Тренировки по алгоритмам (`https://yandex.ru/yaintern/training/algorithm-training`),
|
||||||
`https://yandex.ru/yaintern/training/algorithm-training`), классика LeetCode — по названию
|
классика LeetCode — по названию (ссылки на leetcode.com открываются только в браузере).
|
||||||
(ссылки на leetcode.com открываются только в браузере).
|
|
||||||
|
## Пакет задач (каталог `diag/`)
|
||||||
|
|
||||||
|
- `01..09` — **диагностика**: битовые операции, список, ring buffer, IPv4-заголовок, НВП,
|
||||||
|
потоки, epoll-сервер, bash по логу, разбор падения в gdb.
|
||||||
|
- `10_hash`, `11_heap`, `12_dijkstra`, `13_subnet` — **добор под слабые домены**: своя
|
||||||
|
хеш-таблица с открытой адресацией, куча (`heapify` за O(n), top-K), Дейкстра на
|
||||||
|
`priority_queue`, арифметика подсетей.
|
||||||
|
- `python3 grade.py` — все 13 с автопроверкой (ASAN/UBSAN); `python3 grade.py 10 13` —
|
||||||
|
выборочно; `--fast` — без санитайзеров.
|
||||||
|
- `python3 facts_drill.py` — 60 карточек «числа и факты» (algo 20, linux 14, net 14,
|
||||||
|
c_cpp 12). Маркеры: `>=80%` ок, `50–79%` добор, `<50%` дыра.
|
||||||
|
- Трекер дня: `TRACKER.md` в корне репозитория.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## D0 — 23.09, диагностика (3–4 ч)
|
## D0 — 23.09, диагностика (квиз пройден, 14/32)
|
||||||
|
|
||||||
- Квиз: `python3 quiz.py` (или ответы строкой в чат), 32 вопроса, 4 домена.
|
- Квиз: **пройден** — 14/32, слабейшие домены: алгоритмы, Linux, сети (см. таблицу выше).
|
||||||
- Задачи: `python3 grade.py` — 9 задач с автопроверкой (ASAN/UBSAN).
|
- Осталось: `cd diag && python3 grade.py 01 02 03 04 05 06 07 08 09` — 9 практических задач.
|
||||||
- Разбор промахов вместе со мной, калибровка: какие дни усилить, что срезать.
|
- Разбор промахов квиза вместе со мной, калибровка дней (уже сделана в этой версии плана).
|
||||||
- **Точка:** есть `results_quiz.json` и `results_tasks.json`, названы 3 слабейших темы.
|
- **Точка:** есть `results_quiz.json` и `results_tasks.json`, названы 3 слабейших темы.
|
||||||
|
|
||||||
## D1 — 24.09: массивы/строки + память в C + процессы Linux
|
## D1 — 24.09: инварианты сложности + хеш-таблицы | fork/COW/сигналы
|
||||||
|
|
||||||
- Утро (3 ч) — алгоритмы: два указателя, скользящее окно, префиксные суммы, бинарный
|
- Карточки (30 мин) — домен `algo`: бинарный поиск в 1e6, хеш O(1)/worst O(n), heapify O(n),
|
||||||
поиск и `lower_bound` руками. Задачи: Two Sum, Valid Palindrome, Container With Most
|
устойчивость сортировок, Дейкстра и условие применимости.
|
||||||
Water, Longest Substring Without Repeating Characters, Binary Search + 6–8 задач с
|
- Утро, алгоритмы (3 ч) — **формальные свойства, а не «умение решать»**: O-оценки и
|
||||||
Codeforces по тегам `two pointers` и `binary search`, рейтинг 800–1400.
|
амортизация, хеш-таблицы (цепочки vs открытая адресация, загрузка, tombstone, worst O(n)),
|
||||||
- День (3 ч) — C и память: указатели и арифметика, выравнивание и padding, UB
|
сортировки и устойчивость, бинарный поиск и `lower_bound` руками.
|
||||||
(signed overflow, strict aliasing, неинициализированная память), malloc/free,
|
Практика: **`tasks/10_hash`** — своя таблица на линейном зондировании; 6–8 задач
|
||||||
переполнение буфера; сборка с `-Wall -Wextra -Werror -fsanitize=address,undefined`.
|
Codeforces по тегам `hashing` и `sortings`, рейтинг 800–1400.
|
||||||
Практика: переписать `tasks/01_bits` и `tasks/03_ring` на чистом C (без C++-обёрток),
|
- День, системное (3 ч) — Linux: `fork/exec/wait`, **COW-страницы**, зомби и `waitpid`,
|
||||||
прогнать под санитайзерами.
|
сигналы и коды возврата (137 = 128+9 SIGKILL, 139 = 128+11 SIGSEGV), `errno`.
|
||||||
- Вечер (2 ч) — Linux: `fork/exec/wait`, зомби, сигналы, коды возврата. Практика: свой
|
Практика: мини-шелл (fork + exec + wait + `exit` + пайплайн по желанию) — прямой вопрос
|
||||||
мини-шелл (fork + exec + wait, обработка `exit`) — это прямой вопрос на собеседовании.
|
на собеседовании; прогнать под ASAN.
|
||||||
- **Точка:** 10+ задач решены; мини-шелл запускает `ls`/`cat`; ASAN чистый; лог дня.
|
- Разбор и код (1,5 ч) — `tasks/01_bits` и `tasks/03_ring` **на чистом C** (без C++-обёрток),
|
||||||
|
сборка `-Wall -Wextra -Werror -fsanitize=address,undefined`; одна задача вслух; трекер.
|
||||||
|
- **Точка:** 10_hash PASS; мини-шелл запускает `ls`/`cat`; ASAN чистый; карточки `algo` ≥ 80%.
|
||||||
|
|
||||||
## D2 — 25.09: хеш-таблицы и сортировки + C++ (RAII, владение, STL) + git/Makefile
|
## D2 — 25.09: деревья и куча | epoll и неблокирующие сокеты
|
||||||
|
|
||||||
- Утро (3 ч) — алгоритмы: хеш-таблицы и множества (коллизии, открытая адресация vs
|
- Карточки (30 мин) — `algo`: heap insert O(log n), top-K, обходы деревьев, сложность DFS
|
||||||
цепочки), сортировки и компараторы, stability. Задачи: Group Anagrams, Top K Frequent
|
по памяти.
|
||||||
Elements, Two Sum II, Merge Intervals, Sort Colors + 6–8 задач с тегами `hashing`,
|
- Утро, алгоритмы (3 ч) — бинарные деревья и BST: обходы рекурсией и итерацией,
|
||||||
`sortings`.
|
валидация BST, LCA; куча: **`heapify` снизу вверх за O(n)**, `priority_queue`, top-K,
|
||||||
- День (3 ч) — C++: RAII, правило 0/3/5, move-семантика и `std::move`, умные указатели
|
`nth_element`. Практика: **`tasks/11_heap`** + задачи Invert Binary Tree, Validate BST,
|
||||||
(unique/shared/weak, циклы ссылок), контейнеры STL и итераторы, лямбды, исключения и
|
Kth Largest Element, Top K Frequent Elements.
|
||||||
гарантии безопасности. Практика: свой `unique_ptr` и свой `vector` (упрощённый), класс-
|
- День, системное (3 ч) — Linux: файловый ввод-вывод (`open/read/write/lseek`), **`mmap`**,
|
||||||
владелец с правилом пяти; сборка с `-Werror`.
|
|
||||||
- Вечер (2 ч) — git (ветки, rebase, конфликты, `bisect`) и сборка: Makefile руками, затем
|
|
||||||
CMake для своих задач. Практика: репозиторий с 3 задачами, где сборка/тесты — одной
|
|
||||||
командой.
|
|
||||||
- **Точка:** мини-библиотека собирается с `-Werror`; git-сценарии (rebase, bisect) пройдены
|
|
||||||
руками; 10+ задач.
|
|
||||||
|
|
||||||
## D3 — 26.09: списки/стек + многопоточность + gdb
|
|
||||||
|
|
||||||
- Утро (3 ч) — алгоритмы: связные списки (разворот, цикл Флойда, k-й с конца), стек/
|
|
||||||
очередь/ring buffer, монотонный стек. Задачи: Reverse Linked List, Merge Two Sorted
|
|
||||||
Lists, Linked List Cycle, Valid Parentheses, Daily Temperatures + 6–8 задач с тегами
|
|
||||||
`linked list`, `stack`.
|
|
||||||
- День (3 ч) — многопоточность: `std::thread`, mutex/`lock_guard`, `condition_variable`
|
|
||||||
и predicate-цикл, атомики и `memory_order`, гонки, дедлоки, ложные пробуждения;
|
|
||||||
паттерны producer/consumer и пул потоков. Практика: довести `tasks/06_threads` до
|
|
||||||
продакшн-вида + написать пул потоков. TSan на этой машине недоступен (VMA) — проверяем
|
|
||||||
инвариантами + ASAN, в логе это помечено.
|
|
||||||
- Вечер (2 ч) — gdb: breakpoints, `watch`, `bt`, `frame`, отладка потоков, разбор
|
|
||||||
падения. Практика: `tasks/09_gdb` + своё падение (переполнение буфера) с письменным
|
|
||||||
разбором.
|
|
||||||
- **Точка:** 10+ задач; пул потоков проходит счётчики 4×4×20k; падение разобрано в gdb
|
|
||||||
письменно.
|
|
||||||
|
|
||||||
## D4 — 27.09: деревья/куча + Linux-системное (epoll) + L2/L3
|
|
||||||
|
|
||||||
- Утро (3 ч) — алгоритмы: бинарные деревья и BST, обходы (рекурсия и итерация), куча и
|
|
||||||
`priority_queue`, heapify за O(n), top-K, LCA. Задачи: Invert Binary Tree, Validate
|
|
||||||
Binary Search Tree, Binary Tree Level Order Traversal, Kth Largest Element, Lowest
|
|
||||||
Common Ancestor + 6–8 задач с тегами `trees`, `heaps`.
|
|
||||||
- День (3 ч) — Linux: файловый ввод-вывод (`open/read/write/lseek`), `mmap`,
|
|
||||||
мультиплексирование `select → poll → epoll` (LT/ET), неблокирующие сокеты,
|
мультиплексирование `select → poll → epoll` (LT/ET), неблокирующие сокеты,
|
||||||
`SO_REUSEADDR`, обработка `EAGAIN/EINTR`. Практика: `tasks/07_epoll` — эхо-сервер
|
`SO_REUSEADDR`, `EAGAIN`/`EINTR`. Практика: **`tasks/07_epoll`** — эхо-сервер держит
|
||||||
довести до вида «не падает под нагрузкой» + клиент, нагрузочный тест своими руками.
|
нагрузку, плюс нагрузочный тест своими руками.
|
||||||
- Вечер (2 ч) — сети L2/L3: OSI и TCP/IP, инкапсуляция, Ethernet (14 байт заголовка),
|
- Разбор и код (1,5 ч) — C++ **ровно по промахам**: выравнивание и padding (sizeof 12),
|
||||||
MAC, ARP, VLAN 802.1Q, IPv4-заголовок и контрольная сумма, маски и подсети, ICMP.
|
правило 0/3/5, virtual-деструктор, `std::move` как каст, UB — 45 минут, не больше;
|
||||||
Практика: `tasks/04_ipv4` + два письменных разбора дампов (`tcpdump`: ARP-обмен и
|
одна задача вслух; трекер.
|
||||||
TCP-handshake).
|
- **Точка:** 11_heap и 07_epoll PASS; сервер держит 3+ соединений и не течёт по дескрипторам;
|
||||||
- **Точка:** сервер держит 3+ одновременных соединения и не течёт по дескрипторам;
|
карточки `c_cpp` = 100%.
|
||||||
2 разбора дампов письменно; 10+ задач.
|
|
||||||
|
|
||||||
## D5 — 28.09: графы + TCP глубоко + bash/proc
|
## D3 — 26.09: списки/стек | многопоточность и gdb
|
||||||
|
|
||||||
- Утро (3 ч) — алгоритмы: представления графа, BFS/DFS, топологическая сортировка,
|
- Карточки (30 мин) — `linux`: epoll O(1) против select O(n), mmap, waitpid, 137/139.
|
||||||
компоненты связности, Дейкстра (неотрицательные веса), union-find. Задачи: Number of
|
- Утро, алгоритмы (3 ч) — связные списки (разворот, **цикл Флойда**, k-й с конца),
|
||||||
Islands, Course Schedule, Clone Graph, Network Delay Time, Redundant Connection + 6–8
|
стек/очередь/ring buffer, монотонный стек. Практика: `tasks/02_list`, `tasks/03_ring`
|
||||||
задач с тегами `graphs`, `dfs and similar`, `dsu`.
|
(если ещё не сданы) + Valid Parentheses, Daily Temperatures, Reverse Linked List.
|
||||||
- День (3 ч) — сети глубже: TCP-автомат состояний, окно и подтверждения, ретрансмиссии и
|
- День, системное (3 ч) — многопоточность: `std::thread`, mutex/`lock_guard`,
|
||||||
таймеры, закрытие соединения, UDP, порты, NAT, DNS/DHCP минимум; работа с
|
`condition_variable` и predicate-цикл, атомики и `memory_order`, гонки, дедлоки,
|
||||||
|
ложные пробуждения; паттерны producer/consumer и пул потоков. Практика:
|
||||||
|
`tasks/06_threads` до продакшн-вида + свой пул потоков. (TSan на этой машине может
|
||||||
|
отвалиться по VMA — тогда инварианты + ASAN, в логе это помечено.)
|
||||||
|
- Разбор и код (1,5 ч) — gdb: breakpoints, `watch`, `bt`, `frame`, отладка потоков;
|
||||||
|
`tasks/09_gdb` + **своё падение** с письменным разбором; одна задача вслух; трекер.
|
||||||
|
- **Точка:** пул потоков проходит счётчики 4×4×20k; падение разобрано письменно;
|
||||||
|
карточки `linux` ≥ 80%.
|
||||||
|
|
||||||
|
## D4 — 27.09: графы | L2/L3 и подсети
|
||||||
|
|
||||||
|
- Карточки (30 мин) — `net`: 14 байт Ethernet, VLAN +4, /26 = 62, TTL, SYN/SYN-ACK/ACK.
|
||||||
|
- Утро, алгоритмы (3 ч) — графы: представления, BFS/DFS, топологическая сортировка
|
||||||
|
(только для DAG), компоненты связности, **Дейкстра (веса ≥ 0)**, union-find.
|
||||||
|
Практика: **`tasks/12_dijkstra`** + Number of Islands, Course Schedule, Network Delay
|
||||||
|
Time, Redundant Connection + 6–8 задач с тегами `graphs`, `dsu`.
|
||||||
|
- День, системное (3 ч) — сети L2/L3: OSI и TCP/IP, инкапсуляция, Ethernet (14 байт),
|
||||||
|
MAC, ARP, VLAN 802.1Q (+4 байта), IPv4-заголовок (20 байт) и контрольная сумма, **TTL**,
|
||||||
|
маски и подсети, ICMP. Практика: `tasks/04_ipv4` + **`tasks/13_subnet`** + два письменных
|
||||||
|
разбора дампов (`tcpdump`: ARP-обмен и TCP-handshake).
|
||||||
|
- Разбор и код (1,5 ч) — задача вслух (граф или подсеть); разбор промахов дня; трекер.
|
||||||
|
- **Точка:** 12_dijkstra и 13_subnet PASS; 2 разбора дампов письменно; карточки `net` ≥ 80%.
|
||||||
|
|
||||||
|
## D5 — 28.09: ДП | TCP глубоко, bash и /proc
|
||||||
|
|
||||||
|
- Карточки (30 мин) — `net` + `c_cpp` (20 байт IPv4/TCP, MTU 1500, DNS 53, ASAN/UBSAN).
|
||||||
|
- Утро, алгоритмы (3 ч) — ДП: состояния и переходы, рюкзак 0/1, **НВП за O(n log n)**,
|
||||||
|
строки (Edit Distance, Word Break). Практика: `tasks/05_lis` + Climbing Stairs,
|
||||||
|
House Robber, Coin Change, Edit Distance + 5–6 задач с тегом `dp`.
|
||||||
|
- День, системное (3 ч) — TCP глубоко: автомат состояний, окно и подтверждения,
|
||||||
|
ретрансмиссии и таймеры, закрытие, UDP, порты, NAT, DNS/DHCP минимум; работа с
|
||||||
`tcpdump`/Wireshark. Практика: 3 письменных разбора дампов (ретрансмиссия, ICMP
|
`tcpdump`/Wireshark. Практика: 3 письменных разбора дампов (ретрансмиссия, ICMP
|
||||||
unreachable, DNS) + свой захват трафика своей же программой.
|
unreachable, DNS) + захват трафика своей программой.
|
||||||
- Вечер (2 ч) — Linux-быт: `/proc`, `ulimit`, systemd-юниты обзорно; bash (пайплайны,
|
- Разбор и код (1,5 ч) — bash и Linux-быт: пайплайны, `set -euo pipefail`, awk/sed,
|
||||||
`set -euo pipefail`, awk/sed для логов). Практика: `tasks/08_bash` + свой скрипт
|
`/proc`, `ulimit`; `tasks/08_bash` (лог в 1e6 строк — только awk/sort, не bash-цикл);
|
||||||
диагностики сети/логов.
|
одна задача вслух; трекер.
|
||||||
- **Точка:** 8+ задач; 3 разбора дампов; скрипт работает на логе в 100k строк за секунды.
|
- **Точка:** 05_lis и 08_bash PASS; 3 разбора дампов; скрипт держит лог 100k строк за секунды.
|
||||||
|
|
||||||
## D6 — 29.09: ДП + ядро/embedded обзорно + docker + резюме
|
## D6 — 29.09: закрытие слабых мест | ядро обзорно, docker, резюме
|
||||||
|
|
||||||
- Утро (3 ч) — алгоритмы: ДП — состояния и переходы, рюкзак, LIS за O(n log n), строковые
|
- Карточки (30 мин) — все домены, полный прогон 60 карточек.
|
||||||
задачи. Задачи: Climbing Stairs, House Robber, Coin Change, Longest Increasing
|
- Утро, алгоритмы (3 ч) — **только слабые места по трекеру**: добить `10..13` до PASS,
|
||||||
Subsequence, Edit Distance, Word Break + 5–6 задач с тегом `dp`, рейтинг 800–1400.
|
повторить hash/BST/heap/Дейкстра/ДП там, где были FAIL или «не знаю».
|
||||||
- День (3 ч) — ядро и embedded обзорно: модуль ядра (сборка, insmod/rmmod, printk,
|
- День, системное (3 ч) — ядро и embedded **обзорно**: модуль (сборка, insmod/rmmod,
|
||||||
параметры, `/proc`), символьный драйвер (`file_operations`), device tree и cross-compile
|
printk, параметры, `/proc`), символьный драйвер (`file_operations`), device tree,
|
||||||
— по касательной, ровно чтобы отвечать словами. Практика: hello-модуль + простейший
|
cross-compile, uboot/bringup — ровно чтобы отвечать словами; docker (образ под
|
||||||
символьный драйвер (в QEMU, если на этой машине сборка не встанет).
|
сборку/тесты) — 45 минут.
|
||||||
- Вечер (2 ч) — docker (образ под сборку/тесты) и **резюме + сопроводительное под Eltex**
|
- Разбор и код (1,5 ч) — **резюме + сопроводительное под Eltex** (учесть офисный формат),
|
||||||
(учесть: рассматривают офисный формат).
|
вопросы работодателю, легенда по опыту.
|
||||||
- **Точка:** модуль грузится/выгружается; черновик резюме отревьюен.
|
- **Точка:** все 13 задач PASS; карточки ≥ 80% во всех доменах; резюме готово к отправке.
|
||||||
|
|
||||||
## D7 — 30.09: повтор, два мок-интервью, выход
|
## D7 — 30.09: повтор и два мок-интервью
|
||||||
|
|
||||||
- 2 ч — утренний повтор: только слабые места из трекера (не всё подряд).
|
- 2 ч — повтор: только карточки с промахами и слабые темы из трекера.
|
||||||
- 3 ч — мок-интервью №1: алгоритмы, 2 задачи вслух с таймером, я веду и разбираю.
|
- 3 ч — мок-интервью №1: алгоритмы, 2 задачи вслух с таймером (я веду и разбираю).
|
||||||
- 2 ч — мок-интервью №2: C/C++ + Linux + сети (вопросы по кругу, включая разбор дампа).
|
- 2 ч — мок-интервью №2: C/C++ + Linux + сети, включая разбор дампа.
|
||||||
- 1 ч — вопросы работодателю, легенда по опыту, финальная вычитка резюме.
|
- 1 ч — вопросы работодателю, легенда по опыту, финальная вычитка резюме.
|
||||||
- **Точка:** ≤ 1 грубая ошибка на сессию; резюме и сопроводительное готовы.
|
- **Точка:** ≤ 1 грубая ошибка на сессию; резюме и сопроводительное готовы.
|
||||||
|
|
||||||
@@ -142,4 +171,4 @@ Wireshark wiki. Задачи: Codeforces с фильтром по тегам и
|
|||||||
|
|
||||||
Незакрытое (полный трек `PLAN.md`): M2 хвост (битовые/строковые алгоритмы, математика),
|
Незакрытое (полный трек `PLAN.md`): M2 хвост (битовые/строковые алгоритмы, математика),
|
||||||
M6 глубина (STP, маршрутизация, HTTP), M7 полностью (Docker в бою, bisect на реальном
|
M6 глубина (STP, маршрутизация, HTTP), M7 полностью (Docker в бою, bisect на реальном
|
||||||
репозитории), M8 ядро (полноценный драйвер, uboot/bringup), M9 электроника.
|
репозитории), M8 ядро (полноценный драйвер, uboot/bringup), M9 электроника и схемотехника.
|
||||||
|
|||||||
+29
-2
@@ -21,6 +21,19 @@
|
|||||||
| 08 | `08_bash` | разбор лога доступа: TOTAL/TOP-3/5XX, 110k строк |
|
| 08 | `08_bash` | разбор лога доступа: TOTAL/TOP-3/5XX, 110k строк |
|
||||||
| 09 | `09_gdb` | отладка ASAN-падения + письменный разбор в `answer.txt` |
|
| 09 | `09_gdb` | отладка ASAN-падения + письменный разбор в `answer.txt` |
|
||||||
|
|
||||||
|
Добор под слабые домены (добавлено 23.09 после диагностики — квиз 14/32):
|
||||||
|
|
||||||
|
| # | Задача | Что проверяется |
|
||||||
|
|---|--------|-----------------|
|
||||||
|
| 10 | `10_hash` | хеш-таблица с открытой адресацией: пробирование, перехеширование, erase |
|
||||||
|
| 11 | `11_heap` | куча: `heapify` снизу вверх O(n), `kth_largest`, `top_k` на 3M |
|
||||||
|
| 12 | `12_dijkstra` | Дейкстра на `priority_queue`: неотрицательные веса, 100k вершин |
|
||||||
|
| 13 | `13_subnet` | арифметика IPv4: сеть/broadcast/узлы, `/31`, `/32`, подбор префикса |
|
||||||
|
|
||||||
|
- `facts.json` + `facts_drill.py` — тренажёр «числа и факты»: 60 карточек
|
||||||
|
(algo 20, linux 14, net 14, c_cpp 12) под конкретные промахи квиза: сложности, байты
|
||||||
|
заголовков, `/26 = 62`, TTL, порядок SYN/SYN-ACK/ACK, паддинг структур, правило трёх/пяти.
|
||||||
|
Маркеры итога: `>=80%` ок, `50–79%` добор, `<50%` дыра.
|
||||||
- `grade.py` — прогон задач с автооценкой (`PASS/FAIL`, число проверок, время).
|
- `grade.py` — прогон задач с автооценкой (`PASS/FAIL`, число проверок, время).
|
||||||
- `solutions/` — **эталонные решения, в руки не отдавать**: они лежат здесь, чтобы
|
- `solutions/` — **эталонные решения, в руки не отдавать**: они лежат здесь, чтобы
|
||||||
автопроверка не «протухла» (эталон → все PASS, заглушка → FAIL).
|
автопроверка не «протухла» (эталон → все PASS, заглушка → FAIL).
|
||||||
@@ -35,9 +48,13 @@ python3 grade.py # все задачи (с ASAN/UBSAN/TSAN)
|
|||||||
python3 grade.py 01 04 # только выбранные
|
python3 grade.py 01 04 # только выбранные
|
||||||
python3 grade.py --fast # без санитайзеров, быстрее
|
python3 grade.py --fast # без санитайзеров, быстрее
|
||||||
python3 grade.py --submit # результаты + свои файлы в submissions/
|
python3 grade.py --submit # результаты + свои файлы в submissions/
|
||||||
|
python3 facts_drill.py # 20 карточек, интерактивно
|
||||||
|
python3 facts_drill.py --all # все 60
|
||||||
|
python3 facts_drill.py --domain algo # только алгоритмы
|
||||||
|
python3 facts_drill.py --answers "20|62|ttl|..." # проверить строку ответов разом
|
||||||
```
|
```
|
||||||
|
|
||||||
Порядок: сначала квиз, потом задачи с 01 по 09. Зависание = FAIL (есть таймауты).
|
Порядок: сначала квиз, потом задачи с 01 по 09 (диагностика), затем 10–13 как добор. Зависание = FAIL (есть таймауты).
|
||||||
Правила честности: без встроенных `popcount`/`reverse`-хелперов, без копирования чужих
|
Правила честности: без встроенных `popcount`/`reverse`-хелперов, без копирования чужих
|
||||||
решений, `answer.txt` в задаче 09 — обязательная часть (оценивается ход разбора).
|
решений, `answer.txt` в задаче 09 — обязательная часть (оценивается ход разбора).
|
||||||
|
|
||||||
@@ -45,10 +62,20 @@ python3 grade.py --submit # результаты + свои файлы в subm
|
|||||||
|
|
||||||
- `results_quiz.json`: балл по каждому домену + разбор промахов с пояснениями.
|
- `results_quiz.json`: балл по каждому домену + разбор промахов с пояснениями.
|
||||||
- `results_tasks.json`: по задаче — PASS/FAIL, число проверок, время, полный лог сборки.
|
- `results_tasks.json`: по задаче — PASS/FAIL, число проверок, время, полный лог сборки.
|
||||||
- Итог и решение по плану (с каких модулей стартовать) собираются по этим двум файлам.
|
- `results_facts.json`: тренажёр карточек — балл по доменам и список промахов.
|
||||||
|
- Итог и решение по плану (с каких модулей стартовать) собираются по этим файлам.
|
||||||
|
- Рабочий план недели: `WEEK7.md` в корне репозитория; трекер дня — `TRACKER.md`.
|
||||||
|
|
||||||
## Публикация и доступ
|
## Публикация и доступ
|
||||||
|
|
||||||
|
Репозиторий: https://git.kodlo.art/Kodlo/eduplan-cpp-eltex — клонируется без пароля,
|
||||||
|
обновляется по ходу недели. Результаты можно присылать веткой `answers/<день>`, файлами
|
||||||
|
в чат или через `grade.py --submit`.
|
||||||
|
|
||||||
|
Репозиторий: https://git.kodlo.art/Kodlo/eduplan-cpp-eltex (клонируется без пароля,
|
||||||
|
обновляется по ходу недели). Результаты можно присылать веткой `answers/<день>`,
|
||||||
|
файлами в чат или через `grade.py --submit`.
|
||||||
|
|
||||||
- Пакет в каталоге: `/home/pi/eduplan/diag` (мир читает; каталог доступен на исполнение).
|
- Пакет в каталоге: `/home/pi/eduplan/diag` (мир читает; каталог доступен на исполнение).
|
||||||
- Инбокс для ответов: `/home/pi/eduplan/diag/submissions/` (0777).
|
- Инбокс для ответов: `/home/pi/eduplan/diag/submissions/` (0777).
|
||||||
- Архив для скачивания: `/home/pi/eduplan/diag/dist/eduplan_diag.tar.gz`.
|
- Архив для скачивания: `/home/pi/eduplan/diag/dist/eduplan_diag.tar.gz`.
|
||||||
|
|||||||
+591
@@ -0,0 +1,591 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"note": "Карточки под промахи квиза 23.09: точные числа, сложности и условия.",
|
||||||
|
"facts": [
|
||||||
|
{
|
||||||
|
"domain": "algo",
|
||||||
|
"q": "Средняя сложность поиска в хеш-таблице при разумной загрузке?",
|
||||||
|
"a": "амортизированное O(1)",
|
||||||
|
"accept": [
|
||||||
|
"o(1)",
|
||||||
|
"амортизированное o(1)",
|
||||||
|
"o(1) амортизированное",
|
||||||
|
"амортизированно o(1)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "algo",
|
||||||
|
"q": "Худшая сложность поиска в хеш-таблице (все ключи в одну корзину)?",
|
||||||
|
"a": "O(n)",
|
||||||
|
"accept": [
|
||||||
|
"o(n)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "algo",
|
||||||
|
"q": "Сколько сравнений в худшем случае у бинарного поиска в массиве из 1 000 000 элементов?",
|
||||||
|
"a": "20",
|
||||||
|
"accept": [
|
||||||
|
"20",
|
||||||
|
"log2 1e6 = 20",
|
||||||
|
"около 20"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "algo",
|
||||||
|
"q": "При каком условии применим алгоритм Дейкстры?",
|
||||||
|
"a": "веса рёбер неотрицательны",
|
||||||
|
"accept": [
|
||||||
|
"неотрицательные веса",
|
||||||
|
"все веса >= 0",
|
||||||
|
"веса неотрицательны",
|
||||||
|
"нет отрицательных рёбер"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "algo",
|
||||||
|
"q": "Чем заменить Дейкстру при отрицательных рёбрах?",
|
||||||
|
"a": "Беллман–Форд",
|
||||||
|
"accept": [
|
||||||
|
"беллман-форд",
|
||||||
|
"беллман форд",
|
||||||
|
"bellman-ford"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "algo",
|
||||||
|
"q": "Сложность построения кучи (heapify) из произвольного массива?",
|
||||||
|
"a": "O(n)",
|
||||||
|
"accept": [
|
||||||
|
"o(n)",
|
||||||
|
"о(n)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "algo",
|
||||||
|
"q": "Сложность вставки одного элемента в кучу?",
|
||||||
|
"a": "O(log n)",
|
||||||
|
"accept": [
|
||||||
|
"o(log n)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "algo",
|
||||||
|
"q": "Какая сортировка устойчивая: merge, quick, heap или selection?",
|
||||||
|
"a": "merge sort",
|
||||||
|
"accept": [
|
||||||
|
"merge",
|
||||||
|
"merge sort",
|
||||||
|
"слиянием",
|
||||||
|
"сортировка слиянием"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "algo",
|
||||||
|
"q": "Худшая сложность quicksort?",
|
||||||
|
"a": "O(n²)",
|
||||||
|
"accept": [
|
||||||
|
"o(n^2)",
|
||||||
|
"o(n²)",
|
||||||
|
"o(n2)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "algo",
|
||||||
|
"q": "Средняя сложность quicksort и mergesort?",
|
||||||
|
"a": "O(n log n)",
|
||||||
|
"accept": [
|
||||||
|
"o(n log n)",
|
||||||
|
"o(n*logn)",
|
||||||
|
"o(nlogn)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "algo",
|
||||||
|
"q": "Для какого графа возможна топологическая сортировка?",
|
||||||
|
"a": "для ациклического (DAG)",
|
||||||
|
"accept": [
|
||||||
|
"dag",
|
||||||
|
"ациклический",
|
||||||
|
"без циклов",
|
||||||
|
"dag (ациклический)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "algo",
|
||||||
|
"q": "Как найти цикл в связном списке за O(1) дополнительной памяти?",
|
||||||
|
"a": "алгоритм Флойда: черепаха и заяц",
|
||||||
|
"accept": [
|
||||||
|
"флойд",
|
||||||
|
"черепаха и заяц",
|
||||||
|
"алгоритм флойда"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "algo",
|
||||||
|
"q": "BFS или DFS даёт кратчайший путь в невзвешенном графе?",
|
||||||
|
"a": "BFS",
|
||||||
|
"accept": [
|
||||||
|
"bfs",
|
||||||
|
"обход в ширину"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "algo",
|
||||||
|
"q": "Сложность Дейкстры на priority_queue (двоичная куча)?",
|
||||||
|
"a": "O((V+E) log V)",
|
||||||
|
"accept": [
|
||||||
|
"o((v+e) log v)",
|
||||||
|
"o((v+e)logv)",
|
||||||
|
"o(e log v)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "algo",
|
||||||
|
"q": "За какую сложность ищется НВП (LIS) в оптимальном решении?",
|
||||||
|
"a": "O(n log n)",
|
||||||
|
"accept": [
|
||||||
|
"o(n log n)",
|
||||||
|
"o(nlogn)",
|
||||||
|
"o(n*log n)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "algo",
|
||||||
|
"q": "Сложность задачи о рюкзаке 0/1 через ДП по вместимости?",
|
||||||
|
"a": "O(n * W)",
|
||||||
|
"accept": [
|
||||||
|
"o(n*w)",
|
||||||
|
"o(nw)",
|
||||||
|
"o(n*w)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "algo",
|
||||||
|
"q": "Насколько быстро работают операции union-find с эвристиками?",
|
||||||
|
"a": "почти O(1), O(α(n))",
|
||||||
|
"accept": [
|
||||||
|
"почти o(1)",
|
||||||
|
"o(alpha(n))",
|
||||||
|
"o(α(n))",
|
||||||
|
"обратная функция аккермана"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "algo",
|
||||||
|
"q": "Какова сложность по памяти рекурсивного DFS в графе в худшем случае?",
|
||||||
|
"a": "O(V) — глубина стека рекурсии",
|
||||||
|
"accept": [
|
||||||
|
"o(v)",
|
||||||
|
"o(h)",
|
||||||
|
"o(v) в худшем случае"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "algo",
|
||||||
|
"q": "Какова нижняя оценка сложности сортировки сравнениями?",
|
||||||
|
"a": "O(n log n)",
|
||||||
|
"accept": [
|
||||||
|
"o(n log n)",
|
||||||
|
"o(nlogn)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "algo",
|
||||||
|
"q": "Структура для поиска слов по префиксу за O(len)?",
|
||||||
|
"a": "trie (префиксное дерево)",
|
||||||
|
"accept": [
|
||||||
|
"trie",
|
||||||
|
"префиксное дерево",
|
||||||
|
"бор"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "linux",
|
||||||
|
"q": "Что делает fork()?",
|
||||||
|
"a": "создаёт новый процесс (свой PID, COW-копия памяти)",
|
||||||
|
"accept": [
|
||||||
|
"создаёт новый процесс",
|
||||||
|
"создает новый процесс",
|
||||||
|
"новый процесс"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "linux",
|
||||||
|
"q": "Что происходит со страницами памяти при fork()?",
|
||||||
|
"a": "copy-on-write: копируются при первой записи",
|
||||||
|
"accept": [
|
||||||
|
"cow",
|
||||||
|
"copy-on-write",
|
||||||
|
"копирование при записи"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "linux",
|
||||||
|
"q": "Какой механизм держит десятки тысяч соединений в одном потоке?",
|
||||||
|
"a": "epoll",
|
||||||
|
"accept": [
|
||||||
|
"epoll",
|
||||||
|
"эполл"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "linux",
|
||||||
|
"q": "Сложность select против epoll на большом числе дескрипторов?",
|
||||||
|
"a": "select — O(n) по всем, epoll — O(1) на готовое событие",
|
||||||
|
"accept": [
|
||||||
|
"select o(n), epoll o(1)",
|
||||||
|
"epoll o(1)",
|
||||||
|
"o(1) на готовое"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "linux",
|
||||||
|
"q": "Что даёт mmap() на файл?",
|
||||||
|
"a": "отображает файл в память; страницы подгружаются по обращению",
|
||||||
|
"accept": [
|
||||||
|
"отображает файл в память",
|
||||||
|
"mmap файла в память",
|
||||||
|
"отображение файла в память"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "linux",
|
||||||
|
"q": "Зачем родителю waitpid()?",
|
||||||
|
"a": "чтобы забрать код возврата и не оставлять зомби",
|
||||||
|
"accept": [
|
||||||
|
"забрать код возврата",
|
||||||
|
"убрать зомби",
|
||||||
|
"чтобы не остались зомби"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "linux",
|
||||||
|
"q": "Что такое процесс-зомби?",
|
||||||
|
"a": "завершившийся процесс, чья запись ждёт wait/waitpid",
|
||||||
|
"accept": [
|
||||||
|
"завершившийся процесс без wait",
|
||||||
|
"ждёт wait",
|
||||||
|
"запись ждёт wait"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "linux",
|
||||||
|
"q": "Код возврата 137 — что это?",
|
||||||
|
"a": "SIGKILL (128+9) — убит",
|
||||||
|
"accept": [
|
||||||
|
"sigkill",
|
||||||
|
"128+9",
|
||||||
|
"убит sigkill"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "linux",
|
||||||
|
"q": "Код возврата 139 — что это?",
|
||||||
|
"a": "SIGSEGV (128+11) — падение по памяти",
|
||||||
|
"accept": [
|
||||||
|
"sigsegv",
|
||||||
|
"128+11",
|
||||||
|
"сегфолт"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "linux",
|
||||||
|
"q": "Что означает errno EAGAIN на неблокирующем сокете?",
|
||||||
|
"a": "данных сейчас нет, повторить позже",
|
||||||
|
"accept": [
|
||||||
|
"повторить позже",
|
||||||
|
"нет данных, попробовать снова",
|
||||||
|
"данных нет"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "linux",
|
||||||
|
"q": "Что такое TID (Thread ID) в /proc?",
|
||||||
|
"a": "номер задачи в /proc/<pid>/task/",
|
||||||
|
"accept": [
|
||||||
|
"номер задачи",
|
||||||
|
"task",
|
||||||
|
"в /proc/<pid>/task"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "linux",
|
||||||
|
"q": "Что делает ulimit -n?",
|
||||||
|
"a": "показывает/меняет лимит открытых файловых дескрипторов",
|
||||||
|
"accept": [
|
||||||
|
"лимит дескрипторов",
|
||||||
|
"лимит открытых файлов",
|
||||||
|
"число открытых файлов"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "linux",
|
||||||
|
"q": "Сигнал с номером 15?",
|
||||||
|
"a": "SIGTERM — запрос на завершение",
|
||||||
|
"accept": [
|
||||||
|
"sigterm",
|
||||||
|
"sigterm (мягкое завершение)",
|
||||||
|
"запрос на завершение"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "linux",
|
||||||
|
"q": "Сколько байт в заголовке TCP без опций?",
|
||||||
|
"a": "20",
|
||||||
|
"accept": [
|
||||||
|
"20",
|
||||||
|
"20 байт"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "net",
|
||||||
|
"q": "Сколько байт в Ethernet-заголовке без VLAN?",
|
||||||
|
"a": "14",
|
||||||
|
"accept": [
|
||||||
|
"14",
|
||||||
|
"14 байт"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "net",
|
||||||
|
"q": "Сколько байт добавляет VLAN-тег 802.1Q?",
|
||||||
|
"a": "4",
|
||||||
|
"accept": [
|
||||||
|
"4",
|
||||||
|
"4 байта"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "net",
|
||||||
|
"q": "Сколько адресов узлов в подсети /26?",
|
||||||
|
"a": "62",
|
||||||
|
"accept": [
|
||||||
|
"62",
|
||||||
|
"62 узла"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "net",
|
||||||
|
"q": "Сколько адресов узлов в подсети /24?",
|
||||||
|
"a": "254",
|
||||||
|
"accept": [
|
||||||
|
"254"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "net",
|
||||||
|
"q": "Какое поле IPv4-заголовка уменьшается на 1 на каждом маршрутизаторе?",
|
||||||
|
"a": "TTL",
|
||||||
|
"accept": [
|
||||||
|
"ttl"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "net",
|
||||||
|
"q": "Правильная последовательность установки TCP-соединения?",
|
||||||
|
"a": "SYN -> SYN-ACK -> ACK",
|
||||||
|
"accept": [
|
||||||
|
"syn, syn-ack, ack",
|
||||||
|
"syn syn-ack ack",
|
||||||
|
"syn -> syn-ack -> ack"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "net",
|
||||||
|
"q": "Что делает ARP?",
|
||||||
|
"a": "находит MAC по IP в локальном сегменте",
|
||||||
|
"accept": [
|
||||||
|
"mac по ip",
|
||||||
|
"ip -> mac",
|
||||||
|
"находит mac по ip"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "net",
|
||||||
|
"q": "Размер MTU Ethernet по умолчанию?",
|
||||||
|
"a": "1500 байт",
|
||||||
|
"accept": [
|
||||||
|
"1500"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "net",
|
||||||
|
"q": "На каком уровне OSI работает обычный коммутатор?",
|
||||||
|
"a": "L2 (канальный)",
|
||||||
|
"accept": [
|
||||||
|
"l2",
|
||||||
|
"канальный",
|
||||||
|
"2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "net",
|
||||||
|
"q": "На каком уровне OSI работает маршрутизатор?",
|
||||||
|
"a": "L3 (сетевой)",
|
||||||
|
"accept": [
|
||||||
|
"l3",
|
||||||
|
"сетевой",
|
||||||
|
"3"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "net",
|
||||||
|
"q": "Сколько байт в минимальном IPv4-заголовке?",
|
||||||
|
"a": "20",
|
||||||
|
"accept": [
|
||||||
|
"20"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "net",
|
||||||
|
"q": "По какому протоколу и порту работает DNS?",
|
||||||
|
"a": "UDP/53 (при больших ответах TCP/53)",
|
||||||
|
"accept": [
|
||||||
|
"udp 53",
|
||||||
|
"53",
|
||||||
|
"udp/53"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "net",
|
||||||
|
"q": "Что означает маска /30 в адресах?",
|
||||||
|
"a": "4 адреса: сеть, 2 узла, broadcast",
|
||||||
|
"accept": [
|
||||||
|
"4 адреса, 2 узла",
|
||||||
|
"2 узла",
|
||||||
|
"сеть+2 узла+broadcast"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "net",
|
||||||
|
"q": "Что делает ICMP Time Exceeded?",
|
||||||
|
"a": "сообщает, что TTL истёк (используется в traceroute)",
|
||||||
|
"accept": [
|
||||||
|
"ttl истёк",
|
||||||
|
"истёк ttl",
|
||||||
|
"сообщает об истечении ttl"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "c_cpp",
|
||||||
|
"q": "Чему равен sizeof(struct S { char a; int b; char c; }) на x86-64 без pack?",
|
||||||
|
"a": "12 (паддинг до выравнивания)",
|
||||||
|
"accept": [
|
||||||
|
"12",
|
||||||
|
"12 байт"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "c_cpp",
|
||||||
|
"q": "Что такое UB?",
|
||||||
|
"a": "undefined behavior: стандарт не накладывает требований",
|
||||||
|
"accept": [
|
||||||
|
"undefined behavior",
|
||||||
|
"неопределённое поведение",
|
||||||
|
"неопределенное поведение"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "c_cpp",
|
||||||
|
"q": "Что делает std::move сам по себе?",
|
||||||
|
"a": "static_cast к rvalue-ссылке; перемещение делает конструктор/присваивание",
|
||||||
|
"accept": [
|
||||||
|
"static_cast к rvalue",
|
||||||
|
"ничего, это каст",
|
||||||
|
"это каст к rvalue"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "c_cpp",
|
||||||
|
"q": "Чем new отличается от malloc?",
|
||||||
|
"a": "new вызывает конструктор и возвращает типизированный указатель",
|
||||||
|
"accept": [
|
||||||
|
"вызывает конструктор",
|
||||||
|
"конструктор",
|
||||||
|
"конструктор + типизированный указатель"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "c_cpp",
|
||||||
|
"q": "Класс владеет ресурсом и имеет деструктор, копирование не объявлено. Что будет?",
|
||||||
|
"a": "сгенерируется поверхностное копирование — двойное освобождение",
|
||||||
|
"accept": [
|
||||||
|
"двойное освобождение",
|
||||||
|
"двойное free",
|
||||||
|
"поверхностное копирование"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "c_cpp",
|
||||||
|
"q": "Зачем нужен виртуальный деструктор у базового класса?",
|
||||||
|
"a": "иначе delete через Base* — UB, деструктор наследника не вызовется",
|
||||||
|
"accept": [
|
||||||
|
"деструктор наследника не вызовется",
|
||||||
|
"иначе ub",
|
||||||
|
"чтобы вызвался деструктор наследника"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "c_cpp",
|
||||||
|
"q": "Что делает const после сигнатуры метода?",
|
||||||
|
"a": "делает this const, запрещает менять поля",
|
||||||
|
"accept": [
|
||||||
|
"this const",
|
||||||
|
"запрещает менять поля",
|
||||||
|
"метод не меняет объект"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "c_cpp",
|
||||||
|
"q": "Определён ли стандартом порядок вычисления аргументов f(a(), b())?",
|
||||||
|
"a": "нет, порядок не задан",
|
||||||
|
"accept": [
|
||||||
|
"нет",
|
||||||
|
"не определён",
|
||||||
|
"не задан",
|
||||||
|
"неопределён"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "c_cpp",
|
||||||
|
"q": "Что проверяет ASAN?",
|
||||||
|
"a": "выходы за границы, use-after-free, утечки",
|
||||||
|
"accept": [
|
||||||
|
"выходы за границы",
|
||||||
|
"use-after-free",
|
||||||
|
"память: границы и use-after-free"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "c_cpp",
|
||||||
|
"q": "Что проверяет UBSAN?",
|
||||||
|
"a": "неопределённое поведение: overflow, сдвиги, выравнивание",
|
||||||
|
"accept": [
|
||||||
|
"undefined behavior",
|
||||||
|
"неопределённое поведение",
|
||||||
|
"overflow"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "c_cpp",
|
||||||
|
"q": "Правило трёх/пяти — в чём суть?",
|
||||||
|
"a": "владеющий ресурсом класс задаёт деструктор, копирование и перемещение",
|
||||||
|
"accept": [
|
||||||
|
"деструктор + копирование + перемещение",
|
||||||
|
"деструктор, копирование, перемещение",
|
||||||
|
"нужно определить деструктор и копирование/перемещение"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "c_cpp",
|
||||||
|
"q": "Чем unique_ptr отличается от shared_ptr?",
|
||||||
|
"a": "unique_ptr — единственный владелец без счётчика; shared_ptr считает ссылки",
|
||||||
|
"accept": [
|
||||||
|
"единственный владелец",
|
||||||
|
"нет счётчика ссылок",
|
||||||
|
"счётчик ссылок у shared_ptr"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,147 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Тренажёр «числа и факты» — добор по промахам квиза (22.09: algo 1/8, linux 3/8, net 3/8).
|
||||||
|
|
||||||
|
Использование (из каталога diag):
|
||||||
|
python3 facts_drill.py # интерактивно, 20 случайных карточек
|
||||||
|
python3 facts_drill.py --count 60 --all # все 60
|
||||||
|
python3 facts_drill.py --domain algo # только алгоритмы
|
||||||
|
python3 facts_drill.py --list # вывести все вопросы с ответами (читать)
|
||||||
|
python3 facts_drill.py --answers "20|62|ttl|..." # проверить строку ответов
|
||||||
|
python3 facts_drill.py --answers "..." --count 60 --seed 42 # тот же порядок, что в прогоне
|
||||||
|
python3 facts_drill.py --selftest # проверить сам набор карточек
|
||||||
|
|
||||||
|
Ответы сравниваются нестрого: регистр, «ё», тире и лишние пробелы не важны.
|
||||||
|
Результат пишется в results_facts.json.
|
||||||
|
"""
|
||||||
|
import argparse
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import random
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
|
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
FACTS_PATH = os.path.join(HERE, "facts.json")
|
||||||
|
|
||||||
|
|
||||||
|
def norm(s: str) -> str:
|
||||||
|
s = (s or "").lower().replace("ё", "е")
|
||||||
|
s = s.replace("–", "-").replace("—", "-").replace("−", "-")
|
||||||
|
s = re.sub(r"[^0-9a-zа-я\-\s]", " ", s)
|
||||||
|
return re.sub(r"\s+", " ", s).strip()
|
||||||
|
|
||||||
|
|
||||||
|
def load():
|
||||||
|
with open(FACTS_PATH, encoding="utf-8") as f:
|
||||||
|
return json.load(f)["facts"]
|
||||||
|
|
||||||
|
|
||||||
|
def check(fact, answer: str) -> bool:
|
||||||
|
a = norm(answer)
|
||||||
|
if not a:
|
||||||
|
return False
|
||||||
|
return any(a == norm(x) for x in fact.get("accept", []))
|
||||||
|
|
||||||
|
|
||||||
|
def selftest(facts) -> int:
|
||||||
|
problems = []
|
||||||
|
for i, f in enumerate(facts, 1):
|
||||||
|
for key in ("domain", "q", "a", "accept"):
|
||||||
|
if not f.get(key):
|
||||||
|
problems.append(f"карточка {i}: пустое поле {key}")
|
||||||
|
if f.get("accept") and not any(norm(x) for x in f["accept"]):
|
||||||
|
problems.append(f"карточка {i}: варианты ответа нормализуются в пустоту")
|
||||||
|
if not check(f, f["accept"][0]):
|
||||||
|
problems.append(f"карточка {i}: канонический ответ не проходит проверку")
|
||||||
|
doms = {}
|
||||||
|
for f in facts:
|
||||||
|
doms[f["domain"]] = doms.get(f["domain"], 0) + 1
|
||||||
|
print(f"карточек: {len(facts)}; по доменам: {doms}")
|
||||||
|
if problems:
|
||||||
|
print("ПРОБЛЕМЫ:")
|
||||||
|
for p in problems:
|
||||||
|
print(" -", p)
|
||||||
|
return 1
|
||||||
|
print("SELFTEST OK")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
ap = argparse.ArgumentParser()
|
||||||
|
ap.add_argument("--answers", default=None, help="ответы через | (тот же порядок, что в интерактиве)")
|
||||||
|
ap.add_argument("--count", type=int, default=20)
|
||||||
|
ap.add_argument("--all", action="store_true", help="взять все карточки")
|
||||||
|
ap.add_argument("--domain", default=None, choices=["algo", "linux", "net", "c_cpp"])
|
||||||
|
ap.add_argument("--seed", type=int, default=42)
|
||||||
|
ap.add_argument("--list", action="store_true")
|
||||||
|
ap.add_argument("--selftest", action="store_true")
|
||||||
|
args = ap.parse_args()
|
||||||
|
|
||||||
|
facts = load()
|
||||||
|
if args.selftest:
|
||||||
|
sys.exit(selftest(facts))
|
||||||
|
if args.domain:
|
||||||
|
facts = [f for f in facts if f["domain"] == args.domain]
|
||||||
|
if not facts:
|
||||||
|
print("нет карточек под фильтр")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
if args.list:
|
||||||
|
for i, f in enumerate(facts, 1):
|
||||||
|
print(f"{i:2d}. [{f['domain']}] {f['q']}")
|
||||||
|
print(f" -> {f['a']}")
|
||||||
|
print(f"\nвсего: {len(facts)} (полный ответ в facts.json: {FACTS_PATH})")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
rng = random.Random(args.seed)
|
||||||
|
pool = facts[:]
|
||||||
|
rng.shuffle(pool)
|
||||||
|
picked = pool if args.all else pool[:min(args.count, len(pool))]
|
||||||
|
|
||||||
|
results = []
|
||||||
|
if args.answers is not None:
|
||||||
|
given = [x.strip() for x in args.answers.split("|")]
|
||||||
|
if len(given) < len(picked):
|
||||||
|
print(f"ответов меньше, чем карточек: {len(given)} < {len(picked)} (проверяю сколько есть)")
|
||||||
|
for i, f in enumerate(picked):
|
||||||
|
ans = given[i] if i < len(given) else ""
|
||||||
|
results.append({"n": i + 1, "domain": f["domain"], "q": f["q"], "answer": ans,
|
||||||
|
"ok": check(f, ans), "correct": f["a"]})
|
||||||
|
else:
|
||||||
|
print(f"Карточек в прогоне: {len(picked)}. Пустой ввод = не знаю, 'q' = выход.\n")
|
||||||
|
for i, f in enumerate(picked, 1):
|
||||||
|
print(f"[{i}/{len(picked)}] ({f['domain']}) {f['q']}")
|
||||||
|
try:
|
||||||
|
ans = input(" > ").strip()
|
||||||
|
except (EOFError, KeyboardInterrupt):
|
||||||
|
print()
|
||||||
|
break
|
||||||
|
if ans.lower() in ("q", "quit", "выход"):
|
||||||
|
break
|
||||||
|
ok = check(f, ans)
|
||||||
|
print(" ok" if ok else " нет. верно: " + f["a"])
|
||||||
|
results.append({"n": len(results) + 1, "domain": f["domain"], "q": f["q"],
|
||||||
|
"answer": ans, "ok": ok, "correct": f["a"]})
|
||||||
|
|
||||||
|
ok = sum(1 for r in results if r["ok"])
|
||||||
|
total = len(results)
|
||||||
|
per = {}
|
||||||
|
for r in results:
|
||||||
|
d = per.setdefault(r["domain"], {"total": 0, "ok": 0})
|
||||||
|
d["total"] += 1
|
||||||
|
d["ok"] += 1 if r["ok"] else 0
|
||||||
|
with open(os.path.join(HERE, "results_facts.json"), "w", encoding="utf-8") as fh:
|
||||||
|
json.dump({"seed": args.seed, "correct": ok, "total": total,
|
||||||
|
"per_domain": per, "details": results}, fh, ensure_ascii=False, indent=2)
|
||||||
|
|
||||||
|
print(f"\n=== ИТОГ КАРТОЧЕК: {ok}/{total} ===")
|
||||||
|
for d, v in sorted(per.items()):
|
||||||
|
share = 100 * v["ok"] / v["total"] if v["total"] else 0
|
||||||
|
mark = "ок" if share >= 80 else ("добор" if share >= 50 else "дыра")
|
||||||
|
print(f" {d:8s} {v['ok']}/{v['total']} ({share:.0f}%) — {mark}")
|
||||||
|
print("Подробности: results_facts.json")
|
||||||
|
sys.exit(0 if total and ok == total else 1)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -37,6 +37,10 @@ SPEC = {
|
|||||||
"07_epoll": ("bin", "test_epoll.py", 300),
|
"07_epoll": ("bin", "test_epoll.py", 300),
|
||||||
"08_bash": ("bash", "test_bash.py", 300),
|
"08_bash": ("bash", "test_bash.py", 300),
|
||||||
"09_gdb": ("py", "test_gdb.py", 300),
|
"09_gdb": ("py", "test_gdb.py", 300),
|
||||||
|
"10_hash": ("cpp", "test_hash.cpp", 300),
|
||||||
|
"11_heap": ("cpp", "test_heap.cpp", 600),
|
||||||
|
"12_dijkstra":("cpp", "test_dijkstra.cpp",600),
|
||||||
|
"13_subnet": ("cpp", "test_subnet.cpp", 120),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -151,6 +155,7 @@ def main():
|
|||||||
for fn in os.listdir(SUB):
|
for fn in os.listdir(SUB):
|
||||||
os.chmod(os.path.join(SUB, fn), 0o644)
|
os.chmod(os.path.join(SUB, fn), 0o644)
|
||||||
print(f"Ответы скопированы в {SUB}")
|
print(f"Ответы скопированы в {SUB}")
|
||||||
|
print("Пакет: 01-09 диагностика, 10-13 добор по слабым доменам (см. WEEK7.md).")
|
||||||
sys.exit(0 if done == total else 1)
|
sys.exit(0 if done == total else 1)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
// Задача 10 — хеш-таблица с открытой адресацией.
|
||||||
|
// Реализуй методы ниже. Заглушка намеренно не проходит проверку.
|
||||||
|
#include <cstddef>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
class HashTable {
|
||||||
|
public:
|
||||||
|
explicit HashTable(std::size_t initial_capacity = 16) {
|
||||||
|
(void)initial_capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
void put(int key, int value) {
|
||||||
|
(void)key;
|
||||||
|
(void)value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get(int key, int& out) const {
|
||||||
|
(void)key;
|
||||||
|
(void)out;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool erase(int key) {
|
||||||
|
(void)key;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t size() const { return 0; }
|
||||||
|
|
||||||
|
std::size_t capacity() const { return 16; }
|
||||||
|
};
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
# Задача 10 — хеш-таблица с открытой адресацией (C++)
|
||||||
|
|
||||||
|
Свой контейнер, без `std::unordered_map`. Разрешение коллизий — **линейное зондирование**
|
||||||
|
(linear probing), ёмкость — степень двойки, при загрузке > 0.7 таблица **перехешируется**
|
||||||
|
вдвое. Индексация — через маску `idx & (capacity - 1)`, а не `%`.
|
||||||
|
|
||||||
|
```c++
|
||||||
|
class HashTable {
|
||||||
|
public:
|
||||||
|
explicit HashTable(std::size_t initial_capacity = 16);
|
||||||
|
void put(int key, int value); // обновляет значение, если ключ уже есть
|
||||||
|
bool get(int key, int& out) const; // false — ключа нет
|
||||||
|
bool erase(int key); // false — ключа не было
|
||||||
|
std::size_t size() const;
|
||||||
|
std::size_t capacity() const; // степень двойки, >= 16
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Требования:
|
||||||
|
- `put` 100 000 ключей — суммарно быстрее 2 секунд (в среднем O(1));
|
||||||
|
- ключи с совпадающими младшими битами (например, все кратные 16) обязаны находиться —
|
||||||
|
это проверка зондирования, а не «повезло с хешем»;
|
||||||
|
- после 100 000 вставок `capacity()` растёт (степень двойки, загрузка ≤ 0.7), а `size()`
|
||||||
|
честно считает элементы, включая обновления существующих ключей;
|
||||||
|
- удаление должно работать: после `erase` ключ не находится, а поиск другого ключа,
|
||||||
|
стоявшего за ним в цепочке зондирования, по-прежнему работает.
|
||||||
|
|
||||||
|
Проверка: `python3 grade.py 10`. Критерий: все `ok`, сборка без предупреждений,
|
||||||
|
ASAN/UBSAN чистые.
|
||||||
|
|
||||||
|
Разбор после сдачи: почему ёмкость — степень двойки; чем линейное зондирование лучше
|
||||||
|
цепочек по кэшу и хуже по кластеризации; как tombstone-метки спасают поиск после удаления.
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
#include <cstdio>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <random>
|
||||||
|
#include <chrono>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
#include "solution.cpp"
|
||||||
|
|
||||||
|
static int failures = 0;
|
||||||
|
#define CHECK(cond, name) do { if (cond) std::printf("ok %s\n", name); \
|
||||||
|
else { std::printf("FAIL %s (line %d)\n", name, __LINE__); ++failures; } } while (0)
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
{
|
||||||
|
HashTable t;
|
||||||
|
CHECK(t.size() == 0, "новая таблица пуста");
|
||||||
|
CHECK(t.capacity() >= 16 && (t.capacity() & (t.capacity() - 1)) == 0,
|
||||||
|
"capacity — степень двойки и >= 16");
|
||||||
|
int v = -1;
|
||||||
|
CHECK(t.get(42, v) == false, "get по отсутствующему ключу -> false");
|
||||||
|
CHECK(t.erase(42) == false, "erase отсутствующего -> false");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
HashTable t;
|
||||||
|
for (int i = 0; i < 1000; ++i) t.put(i, i * 7);
|
||||||
|
CHECK(t.size() == 1000, "1000 ключей: size == 1000");
|
||||||
|
bool all = true;
|
||||||
|
for (int i = 0; i < 1000; ++i) { int v = -1; if (!t.get(i, v) || v != i * 7) all = false; }
|
||||||
|
CHECK(all, "1000 ключей: все значения читаются");
|
||||||
|
t.put(500, 999);
|
||||||
|
int v = -1;
|
||||||
|
CHECK(t.get(500, v) && v == 999, "повторный put обновляет значение");
|
||||||
|
CHECK(t.size() == 1000, "повторный put не увеличивает size");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// ключи с одинаковыми младшими битами — проверка зондирования
|
||||||
|
HashTable t;
|
||||||
|
for (int i = 0; i < 512; ++i) t.put(i * 16, i);
|
||||||
|
bool all = true;
|
||||||
|
for (int i = 0; i < 512; ++i) { int v = -1; if (!t.get(i * 16, v) || v != i) all = false; }
|
||||||
|
CHECK(all, "512 ключей, кратных 16: все находятся (линейное зондирование)");
|
||||||
|
int missing = -1;
|
||||||
|
CHECK(t.get(15, missing) == false, "нечётный отсутствующий ключ -> false");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// удаление посреди цепочки зондирования
|
||||||
|
HashTable t;
|
||||||
|
for (int i = 0; i < 200; ++i) t.put(i * 16, i);
|
||||||
|
CHECK(t.erase(0) == true, "erase существующего -> true");
|
||||||
|
int v = -1;
|
||||||
|
CHECK(t.get(0, v) == false, "удалённый ключ не найден");
|
||||||
|
bool rest = true;
|
||||||
|
for (int i = 1; i < 200; ++i) { int x = -1; if (!t.get(i * 16, x) || x != i) rest = false; }
|
||||||
|
CHECK(rest, "остальные ключи из цепочки живы после удаления");
|
||||||
|
CHECK(t.size() == 199, "size после удаления == 199");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const int N = 100000;
|
||||||
|
HashTable t;
|
||||||
|
std::mt19937 rng(4242);
|
||||||
|
std::vector<int> keys(N);
|
||||||
|
for (auto& k : keys) k = int(rng() % 1000000);
|
||||||
|
auto t0 = std::chrono::steady_clock::now();
|
||||||
|
for (int i = 0; i < N; ++i) t.put(keys[i], i);
|
||||||
|
double sec = std::chrono::duration<double>(std::chrono::steady_clock::now() - t0).count();
|
||||||
|
std::printf(" (100k вставок: time=%.3fs, capacity=%zu, size=%zu)\n", sec, t.capacity(), t.size());
|
||||||
|
CHECK(sec < 2.0, "100k вставок за < 2 c");
|
||||||
|
CHECK(t.size() >= 95000 && t.size() <= 100000, "size в разумных пределах");
|
||||||
|
CHECK(t.capacity() >= 131072 && t.capacity() <= 262144, "capacity выросла (загрузка <= 0.7)");
|
||||||
|
bool all = true;
|
||||||
|
for (int i = 0; i < N; ++i) { int v = -1; if (!t.get(keys[i], v)) all = false; }
|
||||||
|
CHECK(all, "все 100k ключей читаются");
|
||||||
|
int v = -1;
|
||||||
|
CHECK(t.get(-12345, v) == false, "отсутствующий ключ -> false");
|
||||||
|
}
|
||||||
|
std::printf(failures ? "\nFAILURES: %d\n" : "\nALL PASS\n", failures);
|
||||||
|
return failures ? 1 : 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
// Задача 11 — куча: heapify за O(n), kth_largest, top_k.
|
||||||
|
// Реализуй функции ниже. Заглушка намеренно не проходит проверку.
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
void heapify(std::vector<int>& a) {
|
||||||
|
(void)a;
|
||||||
|
}
|
||||||
|
|
||||||
|
int kth_largest(const std::vector<int>& a, int k) {
|
||||||
|
(void)a;
|
||||||
|
(void)k;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<int> top_k(const std::vector<int>& a, int k) {
|
||||||
|
(void)a;
|
||||||
|
(void)k;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
# Задача 11 — куча: построение за O(n) и top-K (C++)
|
||||||
|
|
||||||
|
Куча **max-heap** в массиве (для элемента `i` дети — `2i+1`, `2i+2`), без `std::priority_queue`.
|
||||||
|
|
||||||
|
```c++
|
||||||
|
void heapify(std::vector<int>& a); // перестроить массив в кучу
|
||||||
|
int kth_largest(const std::vector<int>& a, int k); // k >= 1
|
||||||
|
std::vector<int> top_k(const std::vector<int>& a, int k); // k наибольших, по убыванию
|
||||||
|
```
|
||||||
|
|
||||||
|
Требования:
|
||||||
|
- `heapify` — **просеивание снизу вверх (bottom-up), O(n)**, а не `push` в цикле (это O(n log n));
|
||||||
|
проверка свойств кучи машинная, требование по сложности я сверяю глазами по коду — на
|
||||||
|
собеседовании спросят именно это;
|
||||||
|
- `heapify` обязан сохранить мультимножество элементов (ничего не терять и не дублировать);
|
||||||
|
- `k` в границах `1..n`; при `k > n` — `top_k` возвращает все по убыванию, `kth_largest` при `k > n`
|
||||||
|
возвращает значение минимального элемента (то есть не падает);
|
||||||
|
- 3 000 000 элементов: `heapify` быстрее 2 секунд, `kth_largest` с `k = 1000` быстрее 2 секунд.
|
||||||
|
|
||||||
|
Проверка: `python3 grade.py 11`. Критерий: все `ok`, сборка без предупреждений,
|
||||||
|
ASAN/UBSAN чистые.
|
||||||
|
|
||||||
|
Разбор после сдачи: почему снизу вверх выходит сумма геометрической прогрессии; когда
|
||||||
|
нужен min-heap размера k (top-K на потоке); чем `nth_element` отличается от кучи.
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
#include <cstdio>
|
||||||
|
#include <vector>
|
||||||
|
#include <random>
|
||||||
|
#include <chrono>
|
||||||
|
#include <algorithm>
|
||||||
|
#include "solution.cpp"
|
||||||
|
|
||||||
|
static int failures = 0;
|
||||||
|
#define CHECK(cond, name) do { if (cond) std::printf("ok %s\n", name); \
|
||||||
|
else { std::printf("FAIL %s (line %d)\n", name, __LINE__); ++failures; } } while (0)
|
||||||
|
|
||||||
|
static bool is_max_heap(const std::vector<int>& a) {
|
||||||
|
for (std::size_t i = 1; i < a.size(); ++i)
|
||||||
|
if (a[(i - 1) / 2] < a[i]) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
{
|
||||||
|
std::vector<int> a = {3, 1, 4, 1, 5, 9, 2, 6};
|
||||||
|
std::vector<int> before = a;
|
||||||
|
heapify(a);
|
||||||
|
CHECK(is_max_heap(a), "маленький массив: свойство max-heap держится");
|
||||||
|
std::vector<int> s1 = before, s2 = a;
|
||||||
|
std::sort(s1.begin(), s1.end()); std::sort(s2.begin(), s2.end());
|
||||||
|
CHECK(s1 == s2, "мультимножество сохранено");
|
||||||
|
std::vector<int> empty_a;
|
||||||
|
heapify(empty_a);
|
||||||
|
CHECK(empty_a.empty(), "пустой массив -> пустой");
|
||||||
|
std::vector<int> one = {7};
|
||||||
|
heapify(one);
|
||||||
|
CHECK(one.size() == 1 && one[0] == 7, "один элемент не теряется");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
std::vector<int> a = {1, 2, 3, 4, 5};
|
||||||
|
CHECK(kth_largest(a, 1) == 5, "kth_largest k=1 -> максимум");
|
||||||
|
CHECK(kth_largest(a, 5) == 1, "kth_largest k=n -> минимум");
|
||||||
|
CHECK(kth_largest(a, 3) == 3, "kth_largest k=3 -> медиана");
|
||||||
|
std::vector<int> z = {4, 4, 4};
|
||||||
|
CHECK(kth_largest(z, 2) == 4, "дубликаты: k=2 -> 4");
|
||||||
|
std::vector<int> dup = {5, 5, 1, 1, 3};
|
||||||
|
CHECK(kth_largest(dup, 4) == 1, "дубликаты: k=4 -> 1");
|
||||||
|
std::vector<int> big = top_k(a, 3);
|
||||||
|
CHECK(big.size() == 3 && big[0] == 5 && big[1] == 4 && big[2] == 3, "top_k(3) по убыванию");
|
||||||
|
std::vector<int> all = top_k(a, 10);
|
||||||
|
CHECK(all.size() == 5 && all.front() == 5 && all.back() == 1, "top_k(k>n) -> все по убыванию");
|
||||||
|
std::vector<int> neg = {-5, -1, -9};
|
||||||
|
CHECK(kth_largest(neg, 1) == -1, "отрицательные: максимум -1");
|
||||||
|
CHECK(kth_largest(a, 100) == 1, "k>n не падает и даёт минимум");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const int N = 3000000;
|
||||||
|
std::mt19937 rng(777);
|
||||||
|
std::vector<int> a(N);
|
||||||
|
for (auto& x : a) x = int(rng() % 1000000);
|
||||||
|
std::vector<int> ref = a;
|
||||||
|
auto t0 = std::chrono::steady_clock::now();
|
||||||
|
heapify(a);
|
||||||
|
double sec = std::chrono::duration<double>(std::chrono::steady_clock::now() - t0).count();
|
||||||
|
std::printf(" (3M heapify: time=%.3fs, top=%d)\n", sec, a.empty() ? -1 : a[0]);
|
||||||
|
CHECK(is_max_heap(a), "3M: свойство max-heap держится");
|
||||||
|
CHECK(a[0] == *std::max_element(ref.begin(), ref.end()), "3M: корень == максимум");
|
||||||
|
CHECK(sec < 2.0, "3M heapify за < 2 c");
|
||||||
|
t0 = std::chrono::steady_clock::now();
|
||||||
|
int k = kth_largest(ref, 1000);
|
||||||
|
double sec2 = std::chrono::duration<double>(std::chrono::steady_clock::now() - t0).count();
|
||||||
|
std::sort(ref.begin(), ref.end(), std::greater<int>());
|
||||||
|
std::printf(" (3M kth_largest k=1000: time=%.3fs, value=%d)\n", sec2, k);
|
||||||
|
CHECK(k == ref[999], "3M: kth_largest(k=1000) совпадает с сортировкой");
|
||||||
|
CHECK(sec2 < 3.0, "3M kth_largest за < 3 c");
|
||||||
|
}
|
||||||
|
std::printf(failures ? "\nFAILURES: %d\n" : "\nALL PASS\n", failures);
|
||||||
|
return failures ? 1 : 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
// Задача 12 — кратчайший путь (Дейкстра).
|
||||||
|
// Реализуй функцию ниже. Заглушка намеренно не проходит проверку.
|
||||||
|
#include <vector>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
|
long long shortest_path(int n,
|
||||||
|
const std::vector<std::tuple<int, int, int>>& edges,
|
||||||
|
int src, int dst) {
|
||||||
|
(void)n;
|
||||||
|
(void)edges;
|
||||||
|
(void)src;
|
||||||
|
(void)dst;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
# Задача 12 — Дейкстра на priority_queue (C++)
|
||||||
|
|
||||||
|
```c++
|
||||||
|
long long shortest_path(int n,
|
||||||
|
const std::vector<std::tuple<int,int,int>>& edges, // (u, v, w)
|
||||||
|
int src, int dst);
|
||||||
|
```
|
||||||
|
|
||||||
|
Требования:
|
||||||
|
- рёбра **ориентированные**, вес `w >= 0`, self-loop допустим; вершины `0..n-1`;
|
||||||
|
- вернуть длину кратчайшего пути `src -> dst` (`long long`, веса суммируются до 10^9);
|
||||||
|
- недостижимость → `-1`; `src == dst` → `0`;
|
||||||
|
- вес рёбер 0 допустим (условие применимости Дейкстры — **неотрицательные** веса);
|
||||||
|
- 100 000 вершин и 200 000 рёбер: быстрее 2 секунд. Значит нужна `std::priority_queue`
|
||||||
|
(O((V+E) log V)), а не O(V²) перебор минимума.
|
||||||
|
|
||||||
|
Подсказки по разбору (после сдачи): «ленивое» удаление устаревших записей из очереди
|
||||||
|
(`if (d != dist[v]) continue;`), почему нельзя Дейкстру с отрицательными рёбрами и когда
|
||||||
|
нужен Беллман–Форд.
|
||||||
|
|
||||||
|
Проверка: `python3 grade.py 12`. Критерий: все `ok`, сборка без предупреждений.
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
#include <cstdio>
|
||||||
|
#include <vector>
|
||||||
|
#include <tuple>
|
||||||
|
#include <random>
|
||||||
|
#include <chrono>
|
||||||
|
#include "solution.cpp"
|
||||||
|
|
||||||
|
static int failures = 0;
|
||||||
|
#define CHECK(cond, name) do { if (cond) std::printf("ok %s\n", name); \
|
||||||
|
else { std::printf("FAIL %s (line %d)\n", name, __LINE__); ++failures; } } while (0)
|
||||||
|
|
||||||
|
using E = std::tuple<int, int, int>;
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
{
|
||||||
|
std::vector<E> e = {{0, 1, 4}, {0, 2, 1}, {2, 1, 1}, {1, 3, 1}};
|
||||||
|
CHECK(shortest_path(4, e, 0, 3) == 3, "0->3 через 2: 1+1+1 = 3");
|
||||||
|
CHECK(shortest_path(4, e, 0, 0) == 0, "src == dst -> 0");
|
||||||
|
CHECK(shortest_path(4, e, 3, 0) == -1, "обратного пути нет -> -1");
|
||||||
|
CHECK(shortest_path(4, e, 0, 2) == 1, "прямой путь 0->2 = 1");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
std::vector<E> e = {{0, 1, 5}, {0, 2, 1}, {2, 1, 1}};
|
||||||
|
CHECK(shortest_path(3, e, 0, 1) == 2, "длинное ребро не должно побеждать короткий путь");
|
||||||
|
std::vector<E> z = {{0, 1, 0}, {1, 2, 0}};
|
||||||
|
CHECK(shortest_path(3, z, 0, 2) == 0, "нулевые веса допустимы");
|
||||||
|
std::vector<E> self = {{0, 0, 7}, {0, 1, 2}};
|
||||||
|
CHECK(shortest_path(2, self, 0, 1) == 2, "self-loop не ломает поиск");
|
||||||
|
std::vector<E> par = {{0, 1, 3}, {0, 1, 7}};
|
||||||
|
CHECK(shortest_path(2, par, 0, 1) == 3, "параллельные рёбра: берём минимальное");
|
||||||
|
std::vector<E> none;
|
||||||
|
CHECK(shortest_path(2, none, 0, 1) == -1, "без рёбер -> -1");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// цепочка 0-1-2-...-(n-1) по 1000, плюс «ловушки»: обратные рёбра по 100000.
|
||||||
|
// Ответ детерминирован: идти по цепочке дешевле, чем прыгать назад и снова вперёд.
|
||||||
|
const int n = 100000;
|
||||||
|
std::vector<E> e;
|
||||||
|
e.reserve(200000);
|
||||||
|
for (int i = 0; i + 1 < n; ++i) e.emplace_back(i, i + 1, 1000);
|
||||||
|
for (int i = 1; i < n; ++i) e.emplace_back(i, i - 1, 100000);
|
||||||
|
e.emplace_back(0, 0, 99999999);
|
||||||
|
auto t0 = std::chrono::steady_clock::now();
|
||||||
|
long long d = shortest_path(n, e, 0, n - 1);
|
||||||
|
double sec = std::chrono::duration<double>(std::chrono::steady_clock::now() - t0).count();
|
||||||
|
std::printf(" (100k вершин, 200k рёбер: dist=%lld, time=%.3fs)\n", d, sec);
|
||||||
|
CHECK(d == (long long)(n - 1) * 1000, "цепочка: кратчайший путь по цепочке");
|
||||||
|
CHECK(sec < 2.0, "100k вершин за < 2 c");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// большие веса: сумма должна влезать в long long
|
||||||
|
std::vector<E> e;
|
||||||
|
for (int i = 0; i + 1 < 2000; ++i) e.emplace_back(i, i + 1, 1000000);
|
||||||
|
CHECK(shortest_path(2000, e, 0, 1999) == 1999LL * 1000000LL, "большие веса: 1999 * 1e6");
|
||||||
|
}
|
||||||
|
std::printf(failures ? "\nFAILURES: %d\n" : "\nALL PASS\n", failures);
|
||||||
|
return failures ? 1 : 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
// Задача 13 — арифметика подсетей IPv4.
|
||||||
|
// Реализуй функции ниже. Заглушка намеренно не проходит проверку.
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
struct Subnet {
|
||||||
|
uint32_t network = 0;
|
||||||
|
uint32_t broadcast = 0;
|
||||||
|
uint32_t first_host = 0;
|
||||||
|
uint32_t last_host = 0;
|
||||||
|
long long host_count = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
Subnet subnet_of(uint32_t ip, int prefix) {
|
||||||
|
(void)ip;
|
||||||
|
(void)prefix;
|
||||||
|
return Subnet{};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool same_subnet(uint32_t a, uint32_t b, int prefix) {
|
||||||
|
(void)a;
|
||||||
|
(void)b;
|
||||||
|
(void)prefix;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int prefix_for_hosts(int hosts) {
|
||||||
|
(void)hosts;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
# Задача 13 — арифметика подсетей IPv4 (C++)
|
||||||
|
|
||||||
|
Адреса передаются в **host byte order** (`0x0A000001` = 10.0.0.1), маска — длиной префикса.
|
||||||
|
|
||||||
|
```c++
|
||||||
|
struct Subnet {
|
||||||
|
uint32_t network; // адрес сети
|
||||||
|
uint32_t broadcast; // широковещательный адрес
|
||||||
|
uint32_t first_host; // первый адрес для узла
|
||||||
|
uint32_t last_host; // последний адрес для узла
|
||||||
|
long long host_count; // сколько адресов узлов доступно (для /0 не влезает в int)
|
||||||
|
};
|
||||||
|
|
||||||
|
Subnet subnet_of(uint32_t ip, int prefix); // 0 <= prefix <= 32
|
||||||
|
bool same_subnet(uint32_t a, uint32_t b, int prefix);
|
||||||
|
int prefix_for_hosts(int hosts); // самая узкая подсеть, куда влезает hosts
|
||||||
|
```
|
||||||
|
|
||||||
|
Определения, которые нужно соблюсти:
|
||||||
|
- для `prefix <= 30`: `host_count = 2^(32-prefix) - 2`, `first_host = network + 1`,
|
||||||
|
`last_host = broadcast - 1`;
|
||||||
|
- `/31` (RFC 3021): `host_count = 2`, `first_host = network`, `last_host = broadcast`;
|
||||||
|
- `/32`: `host_count = 1`, `first_host = last_host = network = broadcast = ip`;
|
||||||
|
- `prefix_for_hosts(h)`: **наибольший** `prefix` из диапазона `0..30` (то есть самая узкая
|
||||||
|
подсеть), при котором `host_count >= h`. `h < 1` или `h > 2^32-2` → `-1`.
|
||||||
|
Исключения `/31` и `/32` в подборе не участвуют — они для линков и одиночных адресов.
|
||||||
|
|
||||||
|
Эталонные примеры, которые обязаны сойтись: `/26` → 62 узла; `/24` → 254; `/30` → 2;
|
||||||
|
`10.0.1.130/26` → сеть `10.0.1.128`, broadcast `10.0.1.191`, узлы `129..190`;
|
||||||
|
`prefix_for_hosts(62)` → 26, `prefix_for_hosts(63)` → 25, `prefix_for_hosts(254)` → 24,
|
||||||
|
`prefix_for_hosts(1)` → 30.
|
||||||
|
|
||||||
|
Проверка: `python3 grade.py 13`. Критерий: все `ok`, сборка без предупреждений.
|
||||||
|
|
||||||
|
Разбор после сдачи: как считать префикс по числу узлов за O(1) (`32 - ceil(log2(h+2))`),
|
||||||
|
почему `/31` — исключение, что такое маска в бинарном виде.
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
#include <cstdio>
|
||||||
|
#include <cstdint>
|
||||||
|
#include "solution.cpp"
|
||||||
|
|
||||||
|
static int failures = 0;
|
||||||
|
#define CHECK(cond, name) do { if (cond) std::printf("ok %s\n", name); \
|
||||||
|
else { std::printf("FAIL %s (line %d)\n", name, __LINE__); ++failures; } } while (0)
|
||||||
|
|
||||||
|
static uint32_t ip4(int a, int b, int c, int d) {
|
||||||
|
return (uint32_t(a) << 24) | (uint32_t(b) << 16) | (uint32_t(c) << 8) | uint32_t(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
{
|
||||||
|
Subnet s = subnet_of(ip4(10, 0, 1, 130), 26);
|
||||||
|
CHECK(s.network == ip4(10, 0, 1, 128), "/26: адрес сети 10.0.1.128");
|
||||||
|
CHECK(s.broadcast == ip4(10, 0, 1, 191), "/26: broadcast 10.0.1.191");
|
||||||
|
CHECK(s.first_host == ip4(10, 0, 1, 129), "/26: первый узел 10.0.1.129");
|
||||||
|
CHECK(s.last_host == ip4(10, 0, 1, 190), "/26: последний узел 10.0.1.190");
|
||||||
|
CHECK(s.host_count == 62, "/26: 62 узла (не 64)");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
Subnet s = subnet_of(ip4(192, 168, 5, 200), 24);
|
||||||
|
CHECK(s.network == ip4(192, 168, 5, 0), "/24: сеть 192.168.5.0");
|
||||||
|
CHECK(s.broadcast == ip4(192, 168, 5, 255), "/24: broadcast 192.168.5.255");
|
||||||
|
CHECK(s.host_count == 254, "/24: 254 узла");
|
||||||
|
Subnet t = subnet_of(ip4(172, 16, 4, 9), 30);
|
||||||
|
CHECK(t.network == ip4(172, 16, 4, 8) && t.broadcast == ip4(172, 16, 4, 11),
|
||||||
|
"/30: сеть 172.16.4.8, broadcast 172.16.4.11");
|
||||||
|
CHECK(t.host_count == 2 && t.first_host == ip4(172, 16, 4, 9) && t.last_host == ip4(172, 16, 4, 10),
|
||||||
|
"/30: 2 узла, 9 и 10");
|
||||||
|
Subnet z = subnet_of(ip4(10, 0, 0, 5), 32);
|
||||||
|
CHECK(z.network == ip4(10, 0, 0, 5) && z.broadcast == ip4(10, 0, 0, 5) && z.host_count == 1,
|
||||||
|
"/32: один адрес, сеть == broadcast == узел");
|
||||||
|
Subnet p31 = subnet_of(ip4(10, 0, 0, 1), 31);
|
||||||
|
CHECK(p31.host_count == 2 && p31.first_host == ip4(10, 0, 0, 0) && p31.last_host == ip4(10, 0, 0, 1),
|
||||||
|
"/31: RFC 3021 — 2 узла без вычета");
|
||||||
|
Subnet eight = subnet_of(ip4(10, 200, 30, 40), 8);
|
||||||
|
CHECK(eight.network == ip4(10, 0, 0, 0) && eight.broadcast == ip4(10, 255, 255, 255),
|
||||||
|
"/8: сеть 10.0.0.0, broadcast 10.255.255.255");
|
||||||
|
Subnet zero = subnet_of(ip4(8, 8, 8, 8), 0);
|
||||||
|
CHECK(zero.network == 0 && zero.broadcast == 0xFFFFFFFFu && zero.host_count == 4294967294LL,
|
||||||
|
"/0: вся сеть, узлов 2^32-2");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
CHECK(same_subnet(ip4(10, 0, 1, 130), ip4(10, 0, 1, 150), 26), "/26: 130 и 150 в одной подсети");
|
||||||
|
CHECK(!same_subnet(ip4(10, 0, 1, 130), ip4(10, 0, 1, 200), 26), "/26: 130 и 200 — разные подсети");
|
||||||
|
CHECK(!same_subnet(ip4(10, 0, 1, 130), ip4(10, 0, 1, 127), 26), "/26: 130 и 127 — разные подсети");
|
||||||
|
CHECK(same_subnet(ip4(10, 0, 1, 1), ip4(10, 0, 2, 1), 16), "/16: разные третьи октеты — одна сеть");
|
||||||
|
CHECK(!same_subnet(ip4(10, 0, 1, 1), ip4(10, 1, 1, 1), 16), "/16: 10.0.x и 10.1.x — разные");
|
||||||
|
CHECK(same_subnet(ip4(10, 0, 0, 0), ip4(10, 0, 0, 1), 31), "/31: соседи по RFC 3021");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
CHECK(prefix_for_hosts(62) == 26, "prefix_for_hosts(62) -> 26");
|
||||||
|
CHECK(prefix_for_hosts(63) == 25, "prefix_for_hosts(63) -> 25 (63 в /26 не влезает)");
|
||||||
|
CHECK(prefix_for_hosts(254) == 24, "prefix_for_hosts(254) -> 24");
|
||||||
|
CHECK(prefix_for_hosts(255) == 23, "prefix_for_hosts(255) -> 23");
|
||||||
|
CHECK(prefix_for_hosts(2) == 30, "prefix_for_hosts(2) -> 30");
|
||||||
|
CHECK(prefix_for_hosts(1) == 30, "prefix_for_hosts(1) -> 30 (узкая подсеть общего вида)");
|
||||||
|
CHECK(prefix_for_hosts(0) == -1, "prefix_for_hosts(0) -> -1");
|
||||||
|
CHECK(prefix_for_hosts(4294967295) == -1, "prefix_for_hosts(2^32-1) -> -1 (не влезает)");
|
||||||
|
CHECK(prefix_for_hosts(1000000) == 12, "prefix_for_hosts(1000000) -> 12");
|
||||||
|
}
|
||||||
|
std::printf(failures ? "\nFAILURES: %d\n" : "\nALL PASS\n", failures);
|
||||||
|
return failures ? 1 : 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user