К содержанию

Подтема 7 из 7 · дополнительный урок

Длина и память программы

Ровно L команд, запрет повторять команду, движение в обе стороны: что запоминать, кроме числа.

Ограничение на программу

Дополнительный урок — для продвинутых: такие условия редки.

Сколько программ переводят 2 в 12 командами «+1» и «×2» ровно за пять команд?

Если условие ограничивает программу целиком — длину, последнюю команду, число умножений, — одного текущего числа мало: в состояние добавляют то, что нужно помнить о прошлом. Из 6 в 12 за одну команду попасть можно, а за две — нельзя: «6, и осталась одна команда» и «6, и осталось две» — разные вопросы.

Заданная длина

При заданной длине считают по паре «число, сколько команд осталось»; успех — когда команды кончились ровно в цели. Каждая команда уменьшает остаток на 1.

Вручную
ПрограммаЧисла
111122 → 3 → 4 → 5 → 6 → 12
212112 → 4 → 5 → 10 → 11 → 12

Других пятикомандных программ нет: короткие, например 121 (2 → 3 → 6 → 12), условию не подходят.

Python

Третий аргумент left — сколько команд осталось. запоминает ответы для пар, чтобы не считать их повторно:

from functools import cache  @cachedef f(x, y, left):    if left == 0:        return 1 if x == y else 0    if x >= y:        return 0    return f(x + 1, y, left - 1) + f(x * 2, y, left - 1)  print(f(2, 12, 5))
2

1 if x == y else 0 — короткий выбор: 1, если x равен y, иначе 0.

Тонкости:цель раньше срока

Если цель достигнута, а команды ещё остались, ветка безуспешна: «+1» и «×2» только увеличивают число, и вернуться к цели нельзя. Поэтому x >= y даёт 0. Если команды могут уменьшать число, так отсекать нельзя.

Длина по участкам

А если из 2 в 12 нужно пройти через 6 ровно за пять команд?

При общей длине участки зависят друг от друга: перебирают, сколько команд уходит на первый участок, и складывают произведения. Обычное произведение 3 · 2 = 6 посчитало бы программы любой длины.

Вручную

Из 2 в 6 можно за 2, 3 или 4 команды — по одной программе. Из 6 в 12 — только за одну команду. Сумма 5 команд выходит лишь при 4 + 1: ответ 1 · 1 = 1, программа 11112.

Python
from functools import cache  @cachedef f(x, y, left):    if left == 0:        return 1 if x == y else 0    if x >= y:        return 0    return f(x + 1, y, left - 1) + f(x * 2, y, left - 1)  print(sum(f(2, 6, k) * f(6, 12, 5 - k) for k in range(6)))
1

Состояние

Что ещё, кроме числа, бывает нужно помнить?

Состояние — всё, что нужно знать о прошлом, чтобы считать будущее. Ограничение на одно число проверяют по самому числу; ограничение на программу целиком добавляет в состояние ещё одну величину.

УсловиеСостояние
обычное, запрет числа 9число
ровно L командчисло и сколько команд осталось
умножение ровно один разчисло и было ли умножение
нельзя повторять команду подрядчисло и последняя команда

Возвраты

Команды «+1» и «−1». Сколько программ переводят 2 в 4?

Если число может вернуться туда, где уже было, программ бесконечно много — пока условие не ограничит длину или числа. Перед 2 → 3 → 4 можно пройти круг 2 → 3 → 2 один раз, два, сколько угодно: все эти программы разные.

Поэтому в таких задачах всегда есть рамка: ровно L команд или числа в заданных пределах. Её и переносят в функцию — отсечение «x > y» здесь не годится, а @cache не спасает от бесконечной рекурсии: ответ запоминается, только когда вызов уже завершился.

Запрет повтора подряд

Команды «+1» и «×2». Сколько программ переводят 2 в 20, если нельзя умножать дважды подряд?

Запрет повтора подряд хранят в состоянии как последнюю команду. Из 8, полученного умножением, умножать нельзя, а из 8, полученного прибавлением, — можно.

from functools import cache  @cachedef g(x, y, last):    if x > y:        return 0    if x == y:        return 1    result = g(x + 1, y, 1)    if last != 2:        result += g(x * 2, y, 2)    return result  print(g(2, 20, 0))
22

В начале последней команды нет — передаём 0. Без запрета программ было бы 30.

Сколько чисел

Из 1 командами «+1» и «×2» выполняют не больше четырёх команд. Какое наименьшее число получить нельзя?

Вопрос «сколько чисел» — не «сколько программ»: одинаковые числа считают один раз. Поэтому хранят не счётчик, а чисел, полученных за 0, 1, 2, 3 и 4 команды:

current = {1}reached = {1}for step in range(4):    current = {y for x in current for y in (x + 1, x * 2)}    reached |= currentprint(sorted(reached))print(min(n for n in range(1, 100) if n not in reached))
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 16] 11

Ответ 11. В подсчёте программ множество не годится: оно склеило бы разные программы с одинаковым результатом.

Движение в обе стороны

Команды «+3» и «−2», старт 1, цель 6, ровно пять команд, число на экране всегда положительное. Сколько программ?

Если команды двигают число в обе стороны, рамку задаёт условие — длина и пределы чисел, а не «число больше цели». Из 7 командой «−2» можно вернуться к 5, поэтому ветку выше цели не обрывают.

Вручную

Нужно прибавить 5 за пять команд: если «+3» выполнено a раз, «−2» — b раз, то 3a − 2b = 5 и a + b = 5, откуда a = 3, b = 2. Расстановок двух «−2» среди пяти мест — 10, но половина уводит число в ноль или ниже: например, «−2» первой даёт −1. Подходят 5.

Python
def f(x, left):    if x <= 0:        return 0    if left == 0:        return 1 if x == 6 else 0    return f(x + 3, left - 1) + f(x - 2, left - 1)  print(f(1, 5))
5

Итого

  • Ровно L командчисло и сколько команд осталось
  • Длина по участкамсумма произведений по длине первого участка
  • Не подрядчисло и последняя команда
  • Сколько чиселмножество, а не счётчик
  • Возвратынужна рамка: длина или пределы
  • Обе стороныне обрывать ветку выше цели

Проверьте себя

Ответь сначала сам, потом открой ответ.

Вопрос 1
Сколько программ переводят 2 в 12 командами «+1» и «×2» ровно за четыре команды?
ОтветСкрыть ответ
Ответ
1: 2 → 4 → 5 → 6 → 12. Программа 2 → 3 → 6 → 12 короче — в ней три команды.
Вопрос 2
Что нужно хранить в состоянии, если нельзя умножать два раза подряд?
ОтветСкрыть ответ
Ответ
Текущее число и последнюю выполненную команду.
Вопрос 3
Чем вопрос «сколько чисел можно получить» отличается от «сколько программ»?
ОтветСкрыть ответ
Ответ
Одинаковые числа считают один раз — нужно множество, а не счётчик.
Вопрос 4
Команды «+1» и «×2», старт 0, цель 1. Почему конечного ответа нет?
ОтветСкрыть ответ
Ответ
0 · 2 = 0: умножать можно сколько угодно раз — 1, 21, 221, …

Практика

Для проверки ответов нужен JavaScript. Условия, подсказки и решения доступны ниже.

Ровно пять команд

Команды «+1» и «×2». Сколько программ переводят 2 в 12 ровно за пять команд?

Введите число.

Ровно четыре команды

Команды «+1» и «×2». Сколько программ переводят 2 в 12 ровно за четыре команды?

Введите число.

Пять команд через 6

Команды «+1» и «×2». Сколько программ переводят 2 в 12 ровно за пять команд, если траектория содержит 6? Реши распределением длины между участками.

Введите число.

Без двух умножений подряд

Команды «+1» и «×2». Сколько программ переводят 1 в 16, если нельзя выполнять умножение два раза подряд?

Введите число.

Достижимые числа за пять команд

Команды «+1» и «×2», старт 2, не больше пяти команд. Какое наименьшее число больше 2 получить нельзя?

Введите число.