Правило умножения и перестановки

Считаем число способов выбрать и упорядочить объекты — основа всей комбинаторики.

Перестановка — упорядоченное расположение всех $n$ различных объектов; их число равно $n!$.

Чтобы вычислять вероятности по формуле $P=m/n$, надо уметь считать $m$ и $n$. Этим занимается комбинаторика — искусство подсчёта без перечисления. Зачем уметь считать, не перечисляя? Потому что числа исходов в реальных задачах астрономические: способов раздать колоду из 52 карт больше, чем атомов в наблюдаемой Вселенной. Перечислить их физически невозможно, а формула даёт ответ мгновенно. Комбинаторика — это набор приёмов, превращающих вопрос «сколько способов?» в короткое выражение. И поскольку классическая вероятность есть отношение «благоприятных способов» к «всем способам», без комбинаторики мы не сможем посчитать почти ни одной содержательной вероятности. Начнём с фундамента: правила умножения и перестановок.

Правило умножения

Если первый выбор можно сделать $a$ способами, а второй (при любом первом) — $b$ способами, то пару выборов можно сделать $a\cdot b$ способами. Например, обед из 3 супов и 4 вторых блюд даёт $3\cdot 4=12$ комбинаций. Это правило обобщается на любое число шагов и лежит в основе остальных формул.

Факториал и перестановки

Сколькими способами можно выстроить $n$ различных книг на полке? Первое место — $n$ вариантов, второе — $n-1$ (одна книга уже стоит), и так далее. По правилу умножения получаем

$$n!=n\cdot(n-1)\cdot(n-2)\cdots 2\cdot 1.$$

Это число называют факториалом. По соглашению $0!=1$. Проверим формулу прямым перебором всех перестановок с помощью itertools.permutations.

import math, itertools

for n in range(1, 8):
    items = list(range(n))
    count = sum(1 for _ in itertools.permutations(items))
    print(f"n={n}: перебор={count:>5}, n!={math.factorial(n):>5}")

Вывод:

n=1: перебор=    1, n!=    1
n=2: перебор=    2, n!=    2
n=3: перебор=    6, n!=    6
n=4: перебор=   24, n!=   24
n=5: перебор=  120, n!=  120
n=6: перебор=  720, n!=  720
n=7: перебор= 5040, n!= 5040

Перебор и формула совпадают идеально — здесь нет случайности, мы просто пересчитали все варианты.

Как быстро растёт факториал

Факториал растёт чудовищно быстро: $10!$ уже больше трёх миллионов, а $20!$ превышает $2\cdot 10^{18}$. Поэтому полный перебор перестановок реален лишь для маленьких $n$ — для больших нужна формула, и именно поэтому комбинаторика так ценна.

Случайная перестановка и тасование

Функция random.shuffle равновероятно перемешивает список — каждая из $n!$ перестановок равновозможна. Проверим равномерность на трёх элементах: каждая из $3!=6$ перестановок должна встречаться примерно в $\frac{1}{6}$ случаев.

import random
from collections import Counter
random.seed(0)

cnt = Counter()
for _ in range(600000):
    deck = [1, 2, 3]
    random.shuffle(deck)
    cnt[tuple(deck)] += 1
for perm in sorted(cnt):
    print(perm, round(cnt[perm] / 600000, 4))

Вывод:

(1, 2, 3) 0.1664
(1, 3, 2) 0.1671
(2, 1, 3) 0.1665
(2, 3, 1) 0.1668
(3, 1, 2) 0.1664
(3, 2, 1) 0.1668

Все шесть долей близки к $\frac{1}{6}\approx 0{,}1667$ — тасование действительно равномерно.

Как работает под капотом

Функция itertools.permutations генерирует перестановки лениво, не храня все сразу, поэтому даже большие переборы не съедают память. А random.shuffle реализует алгоритм Фишера—Йетса: он проходит список с конца и меняет каждый элемент со случайным из ещё не обработанных. Можно доказать, что при честном генераторе все $n!$ исходов получаются с равной вероятностью — что мы и увидели в эксперименте.

Частые ошибки

Первая ошибка — забыть, что $0!=1$; это соглашение делает формулы согласованными (пустую полку можно «расставить» ровно одним способом — оставить пустой). Вторая — применять $n!$, когда часть объектов одинакова: перестановки с повторами считаются иначе, через деление на факториалы кратностей. Третья — путать «выбрать и упорядочить все» (перестановки) с «выбрать часть» (размещения и сочетания из следующих уроков).

Итог

  • Правило умножения: число способов последовательных выборов перемножается.
  • Число перестановок $n$ различных объектов равно $n!$.
  • $0!=1$, а факториал растёт быстрее любой степени.
  • Тасование Фишера—Йетса даёт равновероятную случайную перестановку.
Проверьте себя
1. Сколькими способами можно расставить 5 различных книг на полке?
A25
B120
C60
D5
2. Чему по соглашению равен 0!?
A0
B1
CНе определён
DБесконечности
3. Какой алгоритм даёт равновероятную случайную перестановку?
AСортировка пузырьком
BФишера—Йетса (random.shuffle)
CБинарный поиск
DОкругление случайных чисел