Подтема 5 из 6
Маски
Знаки ? и *, проверка fnmatch, перебор с шагом, равным делителю, и построение чисел по маске.
Знаки ? и *
Что означает маска 12*3? и каким числам она соответствует?
Маска — образец записи числа: цифры стоят на своих местах, ? — ровно одна любая цифра, * — любая последовательность цифр, даже пустая. Маска говорит только о записи числа, а делимость проверяют отдельно.
Маске 12?4 соответствуют десять чисел: 1204, 1214, …, 1294. Маске 12*4 — бесконечно много: 124, 1204, 12004, 1234564. Пустая звёздочка и звёздочка «0» — разные числа: 124 и 1204. Разложим по частям маски 12*3? несколько чисел:
Каждый ? заполняется независимо: в маске 1?? цифры не обязаны совпадать. Попробуй сам — поменяй маску или число:
Маска 12*3?, число 120034
Белые клетки — обязательные цифры маски, серые — ?, в оранжевой рамке — *. Тонкая оранжевая черта — пустая *.
Проверка fnmatch
Как проверить в программе, подходит ли число под маску?
Функцией fnmatch(str(n), mask) из модуля fnmatch: маска пишется в ней так же, как в условии. Первый аргумент — запись числа, второй — маска; ответ True или False.
from fnmatch import fnmatch mask = '12*3?'print(fnmatch('1234', mask), fnmatch('120034', mask))print(fnmatch('12304', mask), fnmatch('123', mask))fnmatch сравнивает запись целиком: 912345 под маску 12*3? не подходит, хотя 1234 в ней есть. Границу и делимость она не проверяет — их пишут отдельно. А сравнение строк медленнее остатка, поэтому сначала проверяй делимость, потом маску.
Шаг, равный делителю
Среди чисел, не превышающих 10¹⁰, найди все, что соответствуют маске 3?12?14*5 и делятся на 1917. Десять миллиардов проверок — это часы. Как сократить перебор?
Перебирать только кратные 1917: начать с 0 и шагать по 1917. Чисел станет в 1917 раз меньше, а делимость проверять уже не нужно. Третий аргумент range — шаг:
from fnmatch import fnmatch for n in range(0, 10**10 + 1, 1917): if fnmatch(str(n), '3?12?14*5'): print(n, n // 1917)Пять миллионов кандидатов — пара секунд. Числа идут по возрастанию, так что вывод уже в нужном порядке. 0 тоже кратен 1917, но под маску не подходит, и начинать с него безопасно.
Если нужен остаток — «при делении на 123 даёт остаток 42», — начинают с самого остатка: range(42, limit, 123) перебирает 42, 165, 288, … — ровно числа вида 123 · k + 42.
Кратные внутри отрезка [L; R]
range(L, R + 1, k) идёт по L, L + k, … и даёт кратные k, только если само L кратно k. Для [100; 150] и k = 23 он выдаст 100, 123, 146 — ни одного кратного. Сначала найди первое кратное не меньше L: start = L + (k - L % k) % k. Для 100 это 115.
Числа по маске с ?
Числа до 10¹² по маске 4?8?15?16?23, дающие при делении на 123 остаток 42. Даже с шагом 123 это 8 миллиардов кандидатов. Что делать?
Не искать числа, а построить их по маске: на месте каждого ? — цифра от 0 до 9. Четыре знака ? — это 10⁴ = 10 000 чисел, и все они заведомо подходят под маску. Все наборы из четырёх цифр перебирает из модуля itertools, а число собирает :
from itertools import product found = []for a, b, c, d in product('0123456789', repeat=4): n = int(f'4{a}8{b}15{c}16{d}23') if n % 123 == 42: found.append(n) print(len(found), max(found))product('0123456789', repeat=4) выдаёт наборы ('0', '0', '0', '0'), ('0', '0', '0', '1'), … до ('9', '9', '9', '9'). Подходят 85 чисел, наибольшее — 498915816123. Граница 10¹² здесь ни при чём: все числа маски двенадцатизначные и меньше её.
Если ? стоит первым, ноль на его место ставить нельзя: число не начинается с 0. Для первого знака бери цифры '123456789'.
Звёздочка: product
А если в маске есть *? Длина вставки заранее не известна. Как построить числа по маске 123*678, не превышающие 10⁸, и найти среди них кратные 13?
Оценить, сколько цифр может занять звёздочка, и перебрать вставки каждой длины, начиная с пустой. В маске шесть обязательных цифр, а числа до 10⁸ — не длиннее восьми цифр. Значит, вставка — от 0 до 2 цифр:
from itertools import product for k in range(3): for x in product('0123456789', repeat=k): n = int('123' + ''.join(x) + '678') if n % 13 == 0: print(n, n // 13)''.join(x) склеивает набор цифр в строку. При k = 0 product выдаёт один пустой набор — так проверяется и 123678 без вставки. Нули внутри вставки не теряются: набор ('0', '0') даёт 12300678. Всего кандидатов 1 + 10 + 100 = 111 вместо семи миллионов кратных 13.
С двумя звёздочками перебирают обе длины так, чтобы вместе они не превышали свободное место. Одно и то же число тогда можно собрать по-разному: 1223 по маске 1*2*3 получится, если первая звёздочка пустая, а вторая — «2», и наоборот. Поэтому ответы складывают в , оно хранит каждое число один раз, а в конце выводят sorted(found).
Что выбрать — шаг или построение? Сравни количество кандидатов. Для 3?12?14*5 и делителя 1917 кратных пять миллионов, а построение дало бы 100 · (1 + 10 + 100) = 11 100 чисел — тоже годится. Когда делитель маленький, а маска короткая, построение выигрывает в тысячи раз.
Другие маски
В тренировочных работах знаки маски иногда определены иначе: «? означает одну чётную цифру». Или маску нужно «найти в записи числа». Как быть?
Определение знаков всегда читают в условии и переводят в язык fnmatch: набор цифр в квадратных скобках — одна цифра из набора, звёздочки по краям — «где-то в записи».
from fnmatch import fnmatch # ? — одна чётная цифра: [02468]mask = '7[02468]23[02468]64*8'for n in range(0, 10**10 + 1, 2026): if fnmatch(str(n), mask): print(n)[02468] — одна чётная цифра, [13579] — одна нечётная. Если строишь числа по маске, на месте такого ? перебирай '02468' вместо всех цифр.
«В записи числа можно найти маску 12*45» значит, что перед маской и после неё могут стоять любые цифры. Это та же проверка со звёздочками по краям: fnmatch(str(n), '*12*45*').
Маска встречается и вместе с делителями: «соответствует маске и имеет ровно 8 делителей». Порядок проверок тот же — сначала дешёвые: кратность через шаг, потом маска, и только у прошедших ищи делители.
Итого
- ? и *одна любая цифра; любые цифры, даже ни одной
- Проверка
fnmatch(str(n), mask) - Шаг k, остаток r
range(r, limit + 1, k), для кратных r = 0 - Только ?построить:
product(digits, repeat=q), первая цифра не 0 - Звёздочкадлины вставки от 0 до оценки; две * — во множество
- Чётная цифра
[02468]
Проверьте себя
Ответь сначала сам, потом открой ответ.
12*3??ОтветСкрыть ответ
5?8?1? А маске ?58?ОтветСкрыть ответ
range перебирает числа, не превышающие 10⁹, которые при делении на 37 дают остаток 5?ОтветСкрыть ответ
range(5, 10**9 + 1, 37)1*2, если числа не превышают 10⁶?ОтветСкрыть ответ
123*678 цикл длин начинается с k = 0?ОтветСкрыть ответ
Практика
Для проверки ответов нужен JavaScript. Условия, подсказки и решения доступны ниже.
Отбери числа по маске 12*3?
Какие числа соответствуют маске 12*3??
12341230412003412312934Введите подходящие числа через пробел в том порядке, в котором они перечислены.
Подсказка
Знак ? — ровно одна цифра, * — любое количество цифр, в том числе ни одной.
Решение
Подходят 1234, 120034 и 12934. У 12304 после «3» стоят две цифры, а у 123 после «3» нет цифры для ?.
Решение программой
from fnmatch import fnmatch for s in ["1234", "12304", "120034", "123", "12934"]: if fnmatch(s, "12*3?"): print(s)Программа выведет
123412003412934Ответ: 1234 120034 12934
Найди кратные 7 по маске 1?2
Найди все числа не больше 1000, соответствующие маске 1?2 и делящиеся на 7. Выведи числа и частные.
Запишите все строки ответа подряд в одну строку через пробел: в каждой строке число и частное от деления на 7.
Подсказка
Перебирай только кратные 7 и проверяй маску у их записи.
Решение
Подходят 112 = 7 · 16 и 182 = 7 · 26.
Решение программой
from fnmatch import fnmatch for n in range(7, 1001, 7): if fnmatch(str(n), "1?2"): print(n, n // 7)Программа выведет
112 16182 26Ответ: 112 16 182 26
Найди кратные 2027 по маске 12?4*76
Среди натуральных чисел, не превышающих 10⁸, найди все числа, соответствующие маске 12?4*76 и делящиеся на 2027. Выведи числа в порядке возрастания и частные от деления на 2027.
Запишите все строки ответа подряд в одну строку через пробел: в каждой строке число и частное от деления на 2027.
Подсказка
Кратных 2027 до 10⁸ меньше 50 000 — перебери их все и проверь маску через fnmatch.
Решение
Перебор кратных 2027 даёт четыре числа: 12340376, 12543076, 12745776 и 12948476; частные идут через 100.
Решение программой
from fnmatch import fnmatch for n in range(2027, 10**8 + 1, 2027): if fnmatch(str(n), "12?4*76"): print(n, n // 2027)Программа выведет
12340376 608812543076 618812745776 628812948476 6388Ответ: 12340376 6088 12543076 6188 12745776 6288 12948476 6388
Найди кратные 6131 по маске ?2*34*91
Найди все натуральные числа, меньшие 10⁹, которые делятся на 6131 и соответствуют маске ?2*34*91. Выведи числа в порядке возрастания и частные от деления на 6131.
Запишите все строки ответа подряд в одну строку через пробел: в каждой строке число и частное от деления на 6131.
Подсказка
Перебирай только кратные: range(0, 10**9, 6131) — и проверяй маску через fnmatch.
Решение
Кратных 6131 меньше 10⁹ около 163 тысяч — перебор с шагом проходит их мгновенно.
Решение программой
from fnmatch import fnmatch for n in range(0, 10**9, 6131): if fnmatch(str(n), '?2*34*91'): print(n, n // 6131)Программа выведет
227834091 37161323477691 52761420347491 68561423412991 69061523348291 85361626349091 102161729349891 118961824993491 134561Ответ: 227834091 37161 323477691 52761 420347491 68561 423412991 69061 523348291 85361 626349091 102161 729349891 118961 824993491 134561
Найди кратные 3039 по маске 2?3157*6
Среди натуральных чисел, не превышающих 10¹⁰, найди все числа, соответствующие маске 2?3157*6 и делящиеся на 3039. Выведи числа в порядке возрастания и частные от деления на 3039.
Запишите все строки ответа подряд в одну строку через пробел: в каждой строке число и частное от деления на 3039.
Подсказка
Кратных 3039 до 10¹⁰ около трёх миллионов — перебери их с шагом и проверь маску.
Решение
Шаг 3039 сразу даёт только кратные, проверять делимость не нужно; осталось сравнить запись с маской.
Решение программой
from fnmatch import fnmatch for n in range(0, 10**10 + 1, 3039): if fnmatch(str(n), '2?3157*6'): print(n, n // 3039)Программа выведет
273157476 898842431576836 8001242631573426 8659342831570016 931744Ответ: 273157476 89884 2431576836 800124 2631573426 865934 2831570016 931744
Построй числа по маске 3?8?405
Среди натуральных чисел, не превышающих 10⁷, найди все числа, соответствующие маске 3?8?405 и делящиеся на 53. Выведи числа и частные от деления на 53.
Запишите все строки ответа подряд в одну строку через пробел: в каждой строке число и частное от деления на 53.
Подсказка
В маске нет звёздочки — переменных мест всего два, это 100 записей. Построй их и проверь делимость.
Решение
Только ? — генерируем 100 записей: 3783405 = 53 · 71385 и 3889405 = 53 · 73385.
Решение программой
for a in range(10): for c in range(10): n = int("3" + str(a) + "8" + str(c) + "405") if n % 53 == 0: print(n, n // 53)Программа выведет
3783405 713853889405 73385Ответ: 3783405 71385 3889405 73385
Построй числа по маске 54321?7?9
Среди натуральных чисел, не превышающих 10⁹, найди все числа, соответствующие маске 54321?7?9 и делящиеся на 37. Выведи числа в порядке возрастания и частные от деления на 37.
Запишите все строки ответа подряд в одну строку через пробел: в каждой строке число и частное от деления на 37.
Подсказка
Два знака ? — всего 100 чисел: построй их через product и проверь делимость.
Решение
Все числа маски девятизначные и меньше 10⁹. Наборы цифр product выдаёт по возрастанию, поэтому и числа идут по возрастанию.
Решение программой
from itertools import product for a, b in product('0123456789', repeat=2): n = int(f'54321{a}7{b}9') if n % 37 == 0: print(n, n // 37)Программа выведет
543212799 14681427543215759 14681507543218719 14681587Ответ: 543212799 14681427 543215759 14681507 543218719 14681587
Посчитай числа по маске 4?*7
Сколько чисел соответствует маске 4?*7, если рассматриваются только числа меньше 100 000? Сначала определи допустимые длины звёздочки.
Введите количество чисел.
Подсказка
Сначала определи, сколько цифр может занять звёздочка, если в числе не больше пяти цифр.
Решение
В маске три обязательные цифры: 4, одна цифра вместо ? и 7. Числа меньше 100 000 имеют не более пяти цифр, поэтому длина звёздочки 0, 1 или 2. Всего 10 · (1 + 10 + 100) = 1110.
Решение программой
from fnmatch import fnmatch count = 0for n in range(1, 100000): if fnmatch(str(n), "4?*7"): count += 1print(count)Ответ: 1110
Посчитай числа по маске 1?2*
Сколько чисел, не превышающих 99 999, соответствуют маске 1?2*?
Введите количество чисел.
Подсказка
Обязательных цифр три, а всего цифр не больше пяти.
Решение
1110: длина звёздочки от 0 до 2, на месте ? десять цифр.
Решение программой
from fnmatch import fnmatch count = 0for n in range(1, 100000): if fnmatch(str(n), "1?2*"): count += 1print(count)Ответ: 1110
Построй числа по маске 19*024?7
Среди натуральных чисел, не превышающих 10¹⁰, найди пять наибольших, соответствующих маске 19*024?7, у которых сумма цифр делится на 13. Выведи их в порядке возрастания и частные от деления суммы цифр на 13.
Запишите все строки ответа подряд в одну строку через пробел: в каждой строке число и частное от деления суммы цифр на 13.
Подсказка
Обязательных цифр семь, значит, звёздочка — от 0 до 3 цифр. Построй числа через product, отсортируй и возьми пять последних.
Решение
Кандидатов 10 · (1 + 10 + 100 + 1000) = 11 110. У всех пяти наибольших сумма цифр 52 = 13 · 4.
Решение программой
from itertools import product found = []for k in range(4): for x in product('0123456789', repeat=k): for q in '0123456789': n = int('19' + ''.join(x) + '024' + q + '7') s = sum(int(ch) for ch in str(n)) if s % 13 == 0: found.append((n, s // 13))for n, r in sorted(found)[-5:]: print(n, r)Программа выведет
1999502467 41999602457 41999702447 41999802437 41999902427 4Ответ: 1999502467 4 1999602457 4 1999702447 4 1999802437 4 1999902427 4
Найди наибольшие числа по двум маскам
Среди натуральных чисел, не превышающих 3 · 10⁸, найди пять наибольших чисел, которые соответствуют маске ?3*5*0, не соответствуют маске 2*8* и делятся на 46. Выведи их в порядке возрастания и частные от деления на 46.
Запишите все строки ответа подряд в одну строку через пробел: в каждой строке число и частное от деления на 46.
Подсказка
Иди вниз по кратным 46 от наибольшего не больше 3 · 10⁸ и остановись на пятом; вторая маска — через not fnmatch.
Решение
Перебор по убыванию сразу даёт наибольшие числа. Найденные пять выводим по возрастанию, как просит условие.
Решение программой
from fnmatch import fnmatch found = []for n in range(3 * 10**8 // 46 * 46, 0, -46): s = str(n) if fnmatch(s, '?3*5*0') and not fnmatch(s, '2*8*'): found.append(n) if len(found) == 5: breakfor n in sorted(found): print(n, n // 46)Программа выведет
239995110 5217285239995340 5217290239995570 5217295239996950 5217325239999250 5217375Ответ: 239995110 5217285 239995340 5217290 239995570 5217295 239996950 5217325 239999250 5217375
Найди маску 13*57*2 внутри записи
Найди все числа, меньшие 10⁸, которые делятся на 2971 и в записи которых можно найти маску 13*57*2. Выведи числа в порядке возрастания и частные от деления на 2971.
Запишите все строки ответа подряд в одну строку через пробел: в каждой строке число и частное от деления на 2971.
Подсказка
«Можно найти в записи» — до маски и после неё могут стоять любые цифры: fnmatch(s, '*13*57*2*').
Решение
Например, в 13 571 528 есть 13, потом 57, потом 2 — маска найдена, хотя число кончается на 8.
Решение программой
from fnmatch import fnmatch for n in range(0, 10**8, 2971): if fnmatch(str(n), '*13*57*2*'): print(n, n // 2971)Программа выведет
13345732 449213464572 453213571528 456861357092 2065269913572 23532Ответ: 13345732 4492 13464572 4532 13571528 4568 61357092 20652 69913572 23532
Найди числа по маске с нечётными цифрами
В маске числа знак ? означает одну нечётную цифру, а * — любую последовательность цифр, в том числе пустую. Среди натуральных чисел, не превышающих 10¹⁰, найди все числа, соответствующие маске 5?13?72*4 и делящиеся на 2028. Выведи числа в порядке возрастания и частные от деления на 2028.
Запишите все строки ответа подряд в одну строку через пробел: в каждой строке число и частное от деления на 2028.
Подсказка
Одна нечётная цифра в fnmatch записывается как [13579].
Решение
Маска для fnmatch — 5[13579]13[13579]72*4; кратные 2028 перебираем с шагом.
Решение программой
from fnmatch import fnmatch mask = '5[13579]13[13579]72*4'for n in range(0, 10**10 + 1, 2028): if fnmatch(str(n), mask): print(n, n // 2028)Программа выведет
5113172064 25212885913572964 2915963Ответ: 5113172064 2521288 5913572964 2915963