Подтема 5 из 6
Цепочки до базы и перебор n
Аргумент шагает через несколько чисел или уходит в другую функцию — считаем переходы до базы. А когда спрашивают n — перебираем.
Цепочки с шагом
Что если аргумент уменьшается не на 1, а сразу на 3?
Тогда от каждого n идёт своя цепочка аргументов. Считаем, сколько в ней переходов до базы и на каком числе она в базу попадёт. Пусть F(n) = n при n ≤ 5; F(n) = F(n − 3) + 4 при n > 5.
F(20): аргументы 20, 17, 14, 11, 8, 5 — пять переходов по 4. F(20) = F(5) + 5 · 4 = 25. А у F(21) цепочка другая: 21, 18, 15, 12, 9, 6, 3 — шесть переходов, и попадает она в 3. F(21) = 3 + 6 · 4 = 27.
Поэтому нельзя считать, что цепочка всегда закончится на границе базы: у каждого остатка от деления на шаг — свой конец.
Тонкости:число переходов формулой
После k переходов аргумент равен n − 3k. Нужно наименьшее k, при котором n − 3k ≤ 5, то есть k ≥ (n − 5) / 3 с округлением вверх. В Python: k = (n - 5 + 2) // 3 — прибавка 2 перед делением на 3 и даёт округление вверх. Но сначала уверенно считай переходы по цепочке: формула только сокращает понятное рассуждение.
Разность разных цепочек
Что делать с разностью, если аргументы лежат на разных цепочках? Например: F(n) = n при n ≥ 1000; F(n) = n + F(n + 2) при n < 1000. Найдём F(11) − F(10).
От 11 аргумент идёт по нечётным числам, от 10 — по чётным, и общего значения у них нет. Тогда обе суммы расписывают целиком и вычитают слагаемые парами.
F(11) = 11 + 13 + … + 999 + F(1001) = 11 + 13 + … + 999 + 1001. F(10) = 10 + 12 + … + 998 + F(1000) = 10 + 12 + … + 998 + 1000.
Пар столько, сколько чисел 10, 12, …, 998: (998 − 10) / 2 + 1 = 495. Каждая пара даёт 1, и базы 1001 и 1000 — ещё 1. Ответ: 495 + 1 = 496.
Программой такую задачу решают так же, как задачу темы: поднять предел глубины или прогреть кеш — здесь по убыванию, от базы.
Перебор n
Что если спрашивают не значение, а аргумент — сколько n подходит или какое n наименьшее?
Тогда функцию вызывают для каждого n по очереди: считают подходящие или останавливаются на первом. Пусть F(0) = 0; F(n) = F(n // 2) + n % 2 при n > 0. Сколько чисел от 1 до 63 дают F(n) = 2?
def F(n): if n == 0: return 0 return F(n // 2) + n % 2 count = 0for n in range(1, 64): if F(n) == 2: count += 1print(count)Аргумент здесь каждый раз делится пополам, поэтому вызовы короткие. Почему ответ 15, можно понять и без программы — это в дополнительном уроке.
Наименьшее n ищут циклом : он увеличивает n, пока условие не выполнено. Для функции задачи темы найдём наименьшее n, при котором F(n) > 1 000 000 000:
def F(n): if n == 1: return 1 return n * F(n - 1) n = 1while F(n) <= 10**9: n += 1print(n)Цикл остановился на первом подходящем n — оно и наименьшее: F(12) = 479 001 600, а F(13) = 6 227 020 800.
Тонкости:когда можно остановиться
Останавливаться на первом n с F(n) > K можно, только если функция не убывает. У функции с ветвями из подтемы 1 F(4) = 6, а F(5) = 4: после большого значения может прийти меньшее. Если спрашивают F(n) = K, ищут первое точное совпадение, а границу перебора берут из условия, а не придумывают.
Итого
- Шаг больше 1у каждого остатка своя цепочка, считаем переходы
- Конец цепочкине обязательно на границе базы
- Разные цепочкирасписать обе суммы и вычесть слагаемые парами
- Две функциицепочка одной, потом другой; прибавки внешних шагов — вне скобок
- Сколько nцикл со счётчиком
- Наименьшее nwhile до первого подходящего
Проверьте себя
Ответь сначала сам, потом открой ответ.
ОтветСкрыть ответ
ОтветСкрыть ответ
ОтветСкрыть ответ
ОтветСкрыть ответ
ОтветСкрыть ответ
Практика
Для проверки ответов нужен JavaScript. Условия, подсказки и решения доступны ниже.
Цепочки с шагом 4
F(n) = n при n ≤ 6; F(n) = F(n − 4) + 5 при n > 6. Найди F(51) − F(50).
Введите число.
Подсказка
От 50 и от 51 цепочки попадают в разные базы — пройди обе.
Решение
От 50: 50, 46, …, 10, 6 — одиннадцать переходов, F(50) = 6 + 55 = 61. От 51: 51, 47, …, 7, 3 — двенадцать переходов, F(51) = 3 + 60 = 63. Разность 2, а не 5: цепочки разные.
def F(n): if n <= 6: return n return F(n - 4) + 5 print(F(51) - F(50))Ответ: 2
Разность разных цепочек
F(n) = n при n ≥ 2000; F(n) = n + F(n + 2) при n < 2000. Найди F(21) − F(20).
Введите число.
Подсказка
От 21 и от 20 аргументы идут по разным цепочкам — вычитай слагаемые парами.
Решение
F(21) = 21 + 23 + … + 1999 + F(2001), F(20) = 20 + 22 + … + 1998 + F(2000). Пар 21 − 20, 23 − 22, …, 1999 − 1998 столько, сколько чисел 20, 22, …, 1998: (1998 − 20) / 2 + 1 = 990, каждая даёт 1. Базы 2001 и 2000 — ещё 1. Ответ: 991.
import sys sys.setrecursionlimit(5000) def F(n): if n >= 2000: return n return n + F(n + 2) print(F(21) - F(20))Ответ: 991
Связанные функции в разные стороны
F(n) = F(n − 5) + 10 при n ≥ 20; F(n) = 2 · G(n + 1) при n < 20; G(n) = n при n ≥ 40; G(n) = G(n + 6) + 1 при n < 40. Найди F(48).
Введите число.
Подсказка
Пройди F вниз шагами по 5 до числа меньше 20, затем G — вверх шагами по 6.
Решение
F: 48, 43, 38, 33, 28, 23, 18 — шесть переходов, F(48) = F(18) + 60. F(18) = 2 · G(19). G: 19, 25, 31, 37, 43 — четыре перехода, G(19) = 43 + 4 = 47. F(18) = 94, F(48) = 94 + 60 = 154.
def G(n): if n >= 40: return n return G(n + 6) + 1 def F(n): if n >= 20: return F(n - 5) + 10 return 2 * G(n + 1) print(F(48))Ответ: 154
Сколько аргументов подходит
F(0) = 0; F(n) = F(n // 2) + n % 2 при n > 0. Сколько чисел n от 1 до 100 дают F(n) = 3?
Введите число.
Подсказка
Вызови функцию для каждого n от 1 до 100 и считай совпадения.
Решение
Перебираем n со счётчиком:
def F(n): if n == 0: return 0 return F(n // 2) + n % 2 count = 0for n in range(1, 101): if F(n) == 3: count += 1print(count)Ответ: 33
Задача в формате экзамена: сколько n
Алгоритм вычисления значения функции F(n), где n — целое неотрицательное число, задан следующими соотношениями: F(0) = 0; F(n) = F(n − 1) + 1, если n нечётно; F(n) = F(n / 2), если n > 0 и при этом n чётно. Сколько существует чисел n, для которых 1 ≤ n ≤ 700 и F(n) = 4?
Введите число.
Подсказка
Посчитай F(n) для каждого n от 1 до 700 и считай, сколько раз вышло 4.
Решение
Перебор со счётчиком. Деление на 2 делает вызовы короткими, а n / 2 для чётного n — то же, что n // 2:
def F(n): if n == 0: return 0 elif n % 2 == 1: return F(n - 1) + 1 else: return F(n // 2) count = 0for n in range(1, 701): if F(n) == 4: count += 1print(count)Ответ: 176
Задача в формате экзамена: наименьшее n
Функция F(n), где n — натуральное число, задана следующими соотношениями: F(n) = n, если n < 100; F(n) = n + F(n / 3), если n ≥ 100 и n делится на 3; F(n) = F(n − 1) + 5, если n ≥ 100 и n не делится на 3. Найди наименьшее значение n, для которого F(n) > 2000.
Введите число.
Подсказка
Значения не растут ровно — перебирай n по возрастанию до первого F(n) > 2000.
Решение
Цикл while идёт по n от 1 и останавливается на первом подходящем: F(1340) = 2003. Останавливаться раньше по «кажется, растёт» нельзя: F(1337) = 1995, а F(1338) = 1993.
def F(n): if n < 100: return n elif n % 3 == 0: return n + F(n // 3) else: return F(n - 1) + 5 n = 1while F(n) <= 2000: n += 1print(n)Ответ: 1340