Чему научитесь
- Различать, когда варианты складывают, а когда перемножают
- Выбирать между перестановками, размещениями и сочетаниями
- Ловить двойной счёт проверкой формулы на n = 2 и n = 3
- Считать «хотя бы один» через дополнение и включение-исключение
Как устроено занятие
Сначала разбор с примерами и короткими проверками понимания — они нужны, чтобы поймать непонятое сразу, а не через три темы.
Дальше 15 задач лестницей: разминка, основа, со звёздочкой. Занятие засчитывается, когда решено 10 — остальные не пропадают и учитываются отдельно.
После занятия — вторая часть, ещё 15 задач на те же приёмы в новых сюжетах.
Сколько это займёт
Примерно час-полтора вместе с задачами. Сроков нет: можно закрыть вкладку и вернуться когда удобно — прогресс сохранится.
Считать вместо того, чтобы перебирать
Прошлое занятие было про перебор вариантов. Это — про то, как узнать их количество, ни одного не перебрав.
Разница не косметическая. Подмножеств у тридцати элементов миллиард, и перебрать их нельзя. А сказать, что их ровно , можно мгновенно.
Два правила, из которых растёт всё остальное
Правило произведения. Если выбор делается в несколько шагов и на каждом шаге вариантов известное количество независимо от предыдущих, общее число вариантов — произведение.
Слово из трёх букв в алфавите из 26: . На каждое место буква выбирается независимо.
Правило суммы. Если варианты разбиты на группы, которые не пересекаются, общее число — сумма.
В одной коробке 4 предмета, в другой 7. Взять один предмет: способов. Взять по одному из каждой: .
Ключевое слово в обоих правилах — «не пересекаются» и «независимо». Как только это перестаёт выполняться, правила ломаются, и получается двойной счёт. К этому вернёмся отдельно.
Про размер ответа
Комбинаторные числа растут стремительно: — это 1135 цифр. В Python это не проблема, целые числа неограниченные. В C++ пришлось бы считать по модулю.
Перестановки, размещения, сочетания
Три базовые величины. Различаются они ответами на два вопроса: берём ли все предметы и важен ли порядок.
| что считаем | важен ли порядок | формула |
|---|---|---|
| перестановки: расставить все | да | |
| размещения: выбрать и расставить | да | |
| сочетания: выбрать | нет |
Перестановки. Первый предмет ставим способами, второй — , и так далее. По правилу произведения получается . Договорённость: , потому что пустой набор расставить можно ровно одним способом.
Размещения — то же самое, но останавливаемся после шагов: .
Сочетания получаются из размещений делением. Каждый набор из предметов был посчитан столько раз, сколькими способами его можно расставить, то есть раз. Делим — и получаем .
Это первый пример важнейшего приёма: посчитать с повторами, а потом поделить на кратность.
Как считать сочетания на практике
Через факториалы — громоздко. Удобнее накапливать:
result = 1
for i in range(k):
result = result * (n - i) // (i + 1)
После шагов в result лежит ровно , поэтому деление всегда без остатка. И числа не разрастаются зря — а если считать факториал в тысячу, он будет в две с половиной тысячи цифр.
В Python есть готовое: math.comb(n, k) и math.factorial(n).
Проверка: порядок важен или нет
Из пяти человек выбирают двоих: одного капитаном, другого его заместителем.
Сколько существует вариантов?
Осторожно с двойным счётом
Это главная опасность темы. Перебор, если он неверен, обычно падает или зацикливается. Формула не падает — она молча выдаёт неправильное число.
Как выглядит ошибка
Задача: сколько слов длины в алфавите a, b, c содержат хотя бы одну букву a?
Рассуждение, которое приходит первым: выберем место для буквы a — способов, остальные места заполним чем угодно — способов. Итого .
Проверим при : получается . Выпишем руками: aa, ab, ac, ba, ca. Пять, а не шесть.
Лишним оказалось слово aa: его посчитали дважды — один раз как «a на первом месте», другой раз как «a на втором». Правило произведения здесь неприменимо: по готовому слову нельзя однозначно восстановить, какое место мы «выбирали».
Как не попасться
Проверка на маленьком случае. Возьмите или и выпишите варианты руками. Двадцать секунд, а ловит почти всё.
Считайте дополнение. «Хотя бы один» почти всегда удобнее считать как «все минус ни одного». Здесь: . При : . Сходится.
Спросите себя: восстанавливается ли выбор однозначно? Если по итоговому объекту нельзя понять, каким путём его получили, значит какие-то объекты посчитаны не по одному разу.
Проверка: сколько сочетаний
Сколькими способами можно выбрать 3 предмета из 10, если порядок не важен? Введите целое число.
Включение-исключение
Приём, который превращает «хотя бы один» в точную формулу, когда простое дополнение не помогает.
Задача: сколько чисел от 1 до 100 делятся на 2 или на 3?
Кратных двойке — 50, кратных тройке — 33. Но сложить нельзя: числа вроде 6 и 12 попали в оба списка. Их надо вычесть один раз — а это кратные шестёрке, их 16.
Для трёх условий формула продолжается: прибавляем поодиночке, вычитаем попарные пересечения, возвращаем тройное. Общее правило: наборы нечётного размера прибавляем, чётного — вычитаем.
for mask in range(1, 1 << k): # все непустые наборы условий
... # пересечение условий из набора
total += part if bits % 2 == 1 else -part
Обратите внимание: здесь снова перебор подмножеств из прошлого занятия, но перебираем мы не варианты ответа, а условия. Их обычно мало — десяток, — поэтому невелико.
Тем же приёмом считаются перестановки без неподвижных точек, раскладки без пустых ящиков и многое другое, где сказано «ни одного».
Типичные ошибки
Двойной счёт. Разобран выше. Проверяйте формулу на и руками.
Перепутаны сочетания и размещения. Спросите: «если поменять выбранные предметы местами, это другой вариант?» Разные роли, разные места, разный порядок — значит размещения.
Забыто деление на кратность. Считали с повторами и не поделили. Признак: ответ ровно в раз больше правильного.
Крайние случаи. , , при . Формула должна давать это сама, иначе нужны отдельные проверки.
Нулевая степень. в Python равно единице, и это как раз то, что нужно в комбинаторных формулах. А вот при равно нулю — проверьте, что ваша формула это переживает.
Как проверять себя
- самое маленькое : 0, 1, 2 — и выписать варианты руками;
- и — ответ обычно должен быть единицей;
- на верхней границе — не разрослись ли промежуточные числа сверх нужного;
- сравнение с перебором для : напишите оба решения и сверьте.
Практика: пятнадцать задач
Лестница прежняя: пять разминочных, семь основных, три со звёздочкой. Зачёт при десяти решённых.
Перед тем как писать формулу, проговорите вслух, как устроен выбор: что выбирается первым, что вторым и восстанавливается ли всё однозначно. Это защищает от двойного счёта лучше всего.
А если сомневаетесь — напишите перебор для и сверьте. Приём из прошлого занятия здесь работает как проверка.
Формула, которая считает лишнее
Задача: «дано до ; сколько существует слов длины в алфавите из трёх букв a, b, c, содержащих хотя бы одну букву a? Ответ вывести как есть, он огромный».
Ученик рассуждает: «Выберем место для буквы a — это способов. Остальные мест заполним любыми буквами — это способов». И пишет:
n = int(input())
print(n * 3 ** (n - 1))
Проверьте это рассуждение на маленьком , объясните, что с ним не так, и приведите верную формулу.