Подтема 6 из 6 · дополнительный урок
Когда перебор не успевает
Как посчитать слова длины 20 и больше, когда перебрать их программой уже не получится.
Слова длины 20
Для слов длины 20 перебор уже не успевает: вариантов больше триллиона. Пятибуквенных слов из АБВГ всего 4⁵ = 1024, и перебор с проверкой 'АА' not in word даёт ответ мгновенно. Но для длины 20 вариантов 4²⁰ — больше триллиона.
Нужен способ, в котором число шагов не растёт вместе с числом слов.
Состояния
Чтобы понять, можно ли дописать букву, не нужно знать всё слово. Для запрета АА достаточно знать, заканчивается ли слово на А. Разделим допустимые слова текущей длины на две группы: a — заканчиваются на А, b — на другую букву.
При дописывании буквы новые слова на А получаются только из группы b: a_новое = b. Новые слова на другую букву — из любой группы, и последних букв три: b_новое = 3 · (a + b). Для длины 1 a = 1, b = 3.
- Длина 1: на А одно слово, на другую букву — 3. Дальше каждая строка получается из предыдущей.
- Длина 2: a = 3 — А дописываем только к словам, которые кончаются не на А (прежнее b); b = 3 · (1 + 3) = 12 — другую букву дописываем к любому слову. Всего 15.
- Длина 3: a = 12 — А дописываем только к словам, которые кончаются не на А (прежнее b); b = 3 · (3 + 12) = 45 — другую букву дописываем к любому слову. Всего 57.
- Длина 4: a = 45 — А дописываем только к словам, которые кончаются не на А (прежнее b); b = 3 · (12 + 45) = 171 — другую букву дописываем к любому слову. Всего 216.
- Длина 5: a = 171 — А дописываем только к словам, которые кончаются не на А (прежнее b); b = 3 · (45 + 171) = 648 — другую букву дописываем к любому слову. Всего 819.
Для длины 5 получаем 171 + 648 = 819 — столько же даёт перебор. Для длины 20 меняется одно число в range:
end_a = 1end_other = 3for length in range(2, 21): new_a = end_other new_other = 3 * (end_a + end_other) end_a = new_a end_other = new_other print(end_a + end_other)Девятнадцать шагов вместо триллиона слов. Такой подход называют динамическим программированием: результаты для коротких слов используются для длинных.
Схемы типов
Если важны только типы цифр, перебирают схемы типов, а не сами цифры. Посчитаем двенадцатизначные восьмеричные числа, в записи которых ровно четыре нечётные цифры и никакие две нечётные не стоят рядом. Самих чисел около 6 · 10¹⁰ — перебор не успеет.
Схема — на каждом месте «ч» или «н», их всего 2¹² = 4096. Для подходящей схемы число чисел даёт умножение: нечётная цифра — 4 варианта, чётная — 4, но на первом месте чётная не может быть нулём — 3.
from itertools import product def by_types(length, odd_count): total = 0 for types in product('чн', repeat=length): scheme = ''.join(types) if scheme.count('н') != odd_count: continue if 'нн' in scheme: continue ways = 1 for i, kind in enumerate(scheme): if kind == 'н': ways *= 4 elif i == 0: ways *= 3 else: ways *= 4 total += ways return total brute = 0for d in product(range(8), repeat=5): odd = [x % 2 for x in d] if d[0] != 0 and sum(odd) == 2: if all(odd[i] + odd[i + 1] < 2 for i in range(4)): brute += 1 print(by_types(5, 2), brute)print(by_types(12, 4))Сначала способ проверен на пятизначных числах, где перебор ещё возможен, затем та же by_types считает двенадцатизначные.
Итого
- Оценкаmⁿ вариантов; от миллиардов — не успеть
- Состоянияхранить количества по тому, что нужно для продолжения
- Схемы типовперебрать схемы, каждую посчитать умножением
- Проверкасверить способ с перебором на малых длинах
Проверьте себя
Ответь сначала сам, потом открой ответ.
ОтветСкрыть ответ
А при запрете АА?ОтветСкрыть ответ
АБВГ: на А — 1, на другую букву — 3. Сколько слов длины 2 без АА?ОтветСкрыть ответ
ОтветСкрыть ответ
Практика
Для проверки ответов нужен JavaScript. Условия, подсказки и решения доступны ниже.
Без двух А подряд, длина 6
Сколько шестибуквенных слов из АБВГ не содержит двух букв А подряд? Найди подсчётом по состояниям и проверь перебором.
Введите число.
Подсказка
Состояния: слово кончается на А или на другую букву.
Решение
По состояниям: для длины 5 на А кончается 171 слово, на другую букву — 648. Для длины 6 на А — 648, на другую букву — 3 · (171 + 648) = 2457. Всего 648 + 2457 = 3105.
Решение программой
end_a = 1end_other = 3for length in range(2, 7): new_a = end_other new_other = 3 * (end_a + end_other) end_a = new_a end_other = new_otherprint(end_a + end_other)Ответ: 3105
Без двух Б подряд, длина 30
Сколько тридцатибуквенных слов из АБВ не содержит двух букв Б подряд?
Введите число.
Подсказка
Те же состояния, но других букв две.
Решение
Те же два состояния, но букв, кроме Б, две: на Б — прежнее «не на Б», на другую букву — 2 · (a + b). Двадцать девять шагов дают 13 397 386 067 968.
Решение программой
end_b = 1end_other = 2for length in range(2, 31): new_b = end_other new_other = 2 * (end_b + end_other) end_b = new_b end_other = new_otherprint(end_b + end_other)Ответ: 13397386067968
Двадцать троичных цифр без двух нулей
Сколько существует двадцатизначных троичных чисел, в записи которых нет двух нулей подряд?
Введите число.
Подсказка
Первая цифра не ноль: в начале 0 и 2.
Решение
Состояния — запись кончается на 0 или нет. Первая цифра не ноль, поэтому для длины 1 «на 0» — 0, «не на 0» — 2. Дальше на 0 — прежнее «не на 0», не на 0 — 2 · (a + b). Для длины 20 получается 423 324 672.
Решение программой
end_0 = 0end_other = 2for length in range(2, 21): new_0 = end_other new_other = 2 * (end_0 + end_other) end_0 = new_0 end_other = new_otherprint(end_0 + end_other)Ответ: 423324672
Три чётные цифры не рядом
Сколько существует 10-значных девятеричных чисел, в записи которых ровно три чётные цифры и никакие две чётные не стоят рядом?
Введите число.
Подсказка
Чётная на первом месте — 4 варианта, на остальных — 5.
Решение
Перебираем схемы «ч» и «н» с ровно тремя «ч», никакие две из которых не стоят рядом. Чётная цифра на первом месте — 4 варианта (не 0), на остальных — 5, нечётная — 4. Сумма по схемам — 106 086 400.
Решение программой
from itertools import product total = 0for types in product('чн', repeat=10): scheme = ''.join(types) if scheme.count('ч') != 3 or 'чч' in scheme: continue ways = 1 for i, kind in enumerate(scheme): if kind == 'н': ways *= 4 elif i == 0: ways *= 4 else: ways *= 5 total += waysprint(total)Ответ: 106086400
Два нуля и не более трёх букв
Сколько существует 9-значных 15-ричных чисел, в записи которых ровно два нуля и не более трёх цифр, обозначаемых буквами?
Введите число.
Подсказка
Типы: ноль, цифра-буква (5 вариантов), остальные ненулевые (9).
Решение
Типы цифр — ноль, цифра-буква A–E (5 вариантов) и остальные ненулевые (9 вариантов). Перебираем схемы из девяти типов с двумя нулями не на первом месте и не более чем тремя буквами, каждую считаем умножением: 2 326 478 112.
Решение программой
from itertools import product total = 0for types in product('0bd', repeat=9): scheme = ''.join(types) if scheme[0] == '0' or scheme.count('0') != 2: continue if scheme.count('b') > 3: continue total += 5 ** scheme.count('b') * 9 ** scheme.count('d')print(total)Ответ: 2326478112