К содержанию

Подтема 5 из 6

Маски

Знаки ? и *, проверка fnmatch, перебор с шагом, равным делителю, и построение чисел по маске.

Знаки ? и *

Что означает маска 12*3? и каким числам она соответствует?

Маска — образец записи числа: цифры стоят на своих местах, ? — ровно одна любая цифра, * — любая последовательность цифр, даже пустая. Маска говорит только о записи числа, а делимость проверяют отдельно.

Маске 12?4 соответствуют десять чисел: 1204, 1214, …, 1294. Маске 12*4 — бесконечно много: 124, 1204, 12004, 1234564. Пустая звёздочка и звёздочка «0» — разные числа: 124 и 1204. Разложим по частям маски 12*3? несколько чисел:

Числа по частям маски 12*3?. Звёздочка забирает сколько угодно цифр, в том числе ни одной. Пунктиром — числа, которые под маску не подходят: у 12304 перед последней цифрой 0, а нужна 3; у 123 не хватает цифры для ?.

Каждый ? заполняется независимо: в маске 1?? цифры не обязаны совпадать. Попробуй сам — поменяй маску или число:

Маска 12*3?, число 120034

Подходит. Звёздочка заменяет «00».

Белые клетки — обязательные цифры маски, серые — ?, в оранжевой рамке — *. Тонкая оранжевая черта — пустая *.

Проверка 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))
True True False False

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)
351261495 183235 3212614035 1675855 3412614645 1780185 3712414275 1936575 3912414885 2040905

Пять миллионов кандидатов — пара секунд. Числа идут по возрастанию, так что вывод уже в нужном порядке. 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))
85 498915816123

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)
1237678 95206 12300678 946206 12313678 947206 12326678 948206 12339678 949206 12352678 950206 12365678 951206 12378678 952206 12391678 953206

''.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)
782386498 7023064168 7023864438 7623064068 7623864338

[02468] — одна чётная цифра, [13579] — одна нечётная. Если строишь числа по маске, на месте такого ? перебирай '02468' вместо всех цифр.

«В записи числа можно найти маску 12*45» значит, что перед маской и после неё могут стоять любые цифры. Это та же проверка со звёздочками по краям: fnmatch(str(n), '*12*45*').

Маска встречается и вместе с делителями: «соответствует маске и имеет ровно 8 делителей». Порядок проверок тот же — сначала дешёвые: кратность через шаг, потом маска, и только у прошедших ищи делители.

Итого

  • ? и *одна любая цифра; любые цифры, даже ни одной
  • Проверкаfnmatch(str(n), mask)
  • Шаг k, остаток rrange(r, limit + 1, k), для кратных r = 0
  • Только ?построить: product(digits, repeat=q), первая цифра не 0
  • Звёздочкадлины вставки от 0 до оценки; две * — во множество
  • Чётная цифра[02468]

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

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

Вопрос 1
Какие из чисел 1234, 12304, 123 и 1290034 соответствуют маске 12*3??
ОтветСкрыть ответ
Ответ
1234 (звёздочка пустая) и 1290034 (звёздочка — 900). У 12304 перед последней цифрой 0, а у 123 не хватает цифры для ?.
Вопрос 2
Сколько чисел соответствует маске 5?8?1? А маске ?58?
ОтветСкрыть ответ
Ответ
100 — по десять цифр на каждый ?. У ?58 первая цифра не может быть нулём: 9 чисел, от 158 до 958.
Вопрос 3
Какой range перебирает числа, не превышающие 10⁹, которые при делении на 37 дают остаток 5?
ОтветСкрыть ответ
Ответ
range(5, 10**9 + 1, 37)
Вопрос 4
Сколько цифр может занять звёздочка в маске 1*2, если числа не превышают 10⁶?
ОтветСкрыть ответ
Ответ
От 0 до 4: числа до 10⁶ — не длиннее шести цифр (сам 10⁶ под маску не подходит), а две цифры уже заняты.
Вопрос 5
Зачем в построении по маске 123*678 цикл длин начинается с k = 0?
ОтветСкрыть ответ
Ответ
Звёздочка может быть пустой. При k = 0 product выдаёт один пустой набор, и проверяется число 123678.

Практика

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

Отбери числа по маске 12*3?

Какие числа соответствуют маске 12*3??

12341230412003412312934

Введите подходящие числа через пробел в том порядке, в котором они перечислены.

Найди кратные 7 по маске 1?2

Найди все числа не больше 1000, соответствующие маске 1?2 и делящиеся на 7. Выведи числа и частные.

Запишите все строки ответа подряд в одну строку через пробел: в каждой строке число и частное от деления на 7.

Найди кратные 2027 по маске 12?4*76

Среди натуральных чисел, не превышающих 10⁸, найди все числа, соответствующие маске 12?4*76 и делящиеся на 2027. Выведи числа в порядке возрастания и частные от деления на 2027.

Запишите все строки ответа подряд в одну строку через пробел: в каждой строке число и частное от деления на 2027.

Найди кратные 6131 по маске ?2*34*91

Найди все натуральные числа, меньшие 10⁹, которые делятся на 6131 и соответствуют маске ?2*34*91. Выведи числа в порядке возрастания и частные от деления на 6131.

Запишите все строки ответа подряд в одну строку через пробел: в каждой строке число и частное от деления на 6131.

Найди кратные 3039 по маске 2?3157*6

Среди натуральных чисел, не превышающих 10¹⁰, найди все числа, соответствующие маске 2?3157*6 и делящиеся на 3039. Выведи числа в порядке возрастания и частные от деления на 3039.

Запишите все строки ответа подряд в одну строку через пробел: в каждой строке число и частное от деления на 3039.

Построй числа по маске 3?8?405

Среди натуральных чисел, не превышающих 10⁷, найди все числа, соответствующие маске 3?8?405 и делящиеся на 53. Выведи числа и частные от деления на 53.

Запишите все строки ответа подряд в одну строку через пробел: в каждой строке число и частное от деления на 53.

Построй числа по маске 54321?7?9

Среди натуральных чисел, не превышающих 10⁹, найди все числа, соответствующие маске 54321?7?9 и делящиеся на 37. Выведи числа в порядке возрастания и частные от деления на 37.

Запишите все строки ответа подряд в одну строку через пробел: в каждой строке число и частное от деления на 37.

Посчитай числа по маске 4?*7

Сколько чисел соответствует маске 4?*7, если рассматриваются только числа меньше 100 000? Сначала определи допустимые длины звёздочки.

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

Посчитай числа по маске 1?2*

Сколько чисел, не превышающих 99 999, соответствуют маске 1?2*?

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

Построй числа по маске 19*024?7

Среди натуральных чисел, не превышающих 10¹⁰, найди пять наибольших, соответствующих маске 19*024?7, у которых сумма цифр делится на 13. Выведи их в порядке возрастания и частные от деления суммы цифр на 13.

Запишите все строки ответа подряд в одну строку через пробел: в каждой строке число и частное от деления суммы цифр на 13.

Найди наибольшие числа по двум маскам

Среди натуральных чисел, не превышающих 3 · 10⁸, найди пять наибольших чисел, которые соответствуют маске ?3*5*0, не соответствуют маске 2*8* и делятся на 46. Выведи их в порядке возрастания и частные от деления на 46.

Запишите все строки ответа подряд в одну строку через пробел: в каждой строке число и частное от деления на 46.

Найди маску 13*57*2 внутри записи

Найди все числа, меньшие 10⁸, которые делятся на 2971 и в записи которых можно найти маску 13*57*2. Выведи числа в порядке возрастания и частные от деления на 2971.

Запишите все строки ответа подряд в одну строку через пробел: в каждой строке число и частное от деления на 2971.

Найди числа по маске с нечётными цифрами

В маске числа знак ? означает одну нечётную цифру, а * — любую последовательность цифр, в том числе пустую. Среди натуральных чисел, не превышающих 10¹⁰, найди все числа, соответствующие маске 5?13?72*4 и делящиеся на 2028. Выведи числа в порядке возрастания и частные от деления на 2028.

Запишите все строки ответа подряд в одну строку через пробел: в каждой строке число и частное от деления на 2028.