Подтема 3 из 5
Срезы, замены и сумма цифр
Как брать части записи, менять и переставлять цифры, переворачивать запись и считать сумму цифр.
Части записи
Алгоритм: в двоичной записи 46 = 101110 первые две цифры заменяют на 110. Что получится? Такие команды берут часть записи по её месту — первые или последние цифры — и переставляют, удаляют, заменяют.
Все такие команды собираются из срезов строки и соединения. Здесь же — переворот и инверсия записи и сумма цифр, по которой многие алгоритмы выбирают дописываемую цифру.
Индексы и срезы
Нужную часть записи берут по номерам символов — индексам и срезам. Каждый символ строки имеет номер — индекс. В Python нумерация начинается с нуля: первая цифра записи s = '101110' — это s[0], вторая — s[1]. Отрицательные индексы считают с конца: s[-1] — последний символ, s[-2] — предпоследний.
Чтобы получить несколько символов, используют s[a:b]. Он берёт символы с индексами от a включительно до b не включительно. Например, s[1:4] берёт индексы 1, 2, 3 и даёт '011'.
Если граница не указана, срез идёт от начала или до конца строки. Выбери срез, чтобы увидеть, что он берёт:
- s[:2]
'10'первые два символа- s[-2:]
'10'последние два- s[2:]
'1110'всё, кроме первых двух- s[:-2]
'1011'всё, кроме последних двух- s[::-1]
'011101'строка в обратном порядке
Если позиции в условии считают справа, нельзя без проверки применять срезы для позиций слева. «Вторая справа цифра» — это s[-2], а не s[1].
Сборка из срезов
Почти любое действие задания собирается из срезов и соединения строк. Пусть s — текущая запись. Каждую команду условия можно перевести одной строкой:
- скопировать последние две цифры в конец
s = s + s[-2:]- скопировать первые две цифры в конец
s = s + s[:2]- удалить первые две цифры
s = s[2:]- удалить последние две цифры
s = s[:-2]- первые две цифры заменить на 110
s = '110' + s[2:]- последние две цифры заменить на 01
s = s[:-2] + '01'- перевернуть запись
s = s[::-1]- перенести первую цифру в конец
s = s[1:] + s[0]- перенести последнюю цифру в начало
s = s[-1] + s[:-1]
В строке s = s + s[-2:] обе части справа вычисляются по старому значению s. После этого результат присваивается переменной.
Если после нескольких изменений нужно обратиться именно к исходной записи, сохрани её: original = s. Теперь original содержит исходный текст, а s можно дальше изменять. Сравни два алгоритма:
n = 46original = bin(n)[2:]s = original + '1's = s + original[-2:]print(s) s = bin(n)[2:]s = s + '1's = s + s[-2:]print(s)В первом варианте последние две цифры взяты из исходной записи (10), во втором — уже из изменённой (01). Это два разных алгоритма, и условие всегда говорит, какой из них нужен.
Переворот и инверсия
Перевернуть запись и инвертировать её — разные действия. Перевернуть запись — прочитать её справа налево. Инвертировать двоичную запись — заменить каждый 0 на 1, а каждый 1 на 0. Например, для 1100 переворот даёт 0011, и инверсия тоже даёт 0011. На этом числе результаты случайно совпали. На другом уже нет: для 10110 переворот даёт 01101, а инверсия — 01001.
Поэтому ориентироваться нужно на смысл команды, а не на один пример.
Число из единиц
Инверсию проще понять через число из одних единиц. Возьмём пятизначную двоичную запись 10110. После замены каждого 0 на 1 и каждого 1 на 0 получится 01001. Если сложить значения исходной и изменённой записей, в каждом разряде получится единица:
Длина записи здесь важна: инверсия трёх цифр 101 даёт 010 = 2, а восьми цифр 00000101 — уже 11111010 = 250. Сколько цифр брать, говорит условие.
Тонкости:инверсия формулой
А число из пяти единиц равно 11111₂ = 2⁵ − 1 = 31. Поэтому N + M = 2ᴸ − 1, где L — длина записи, а M — результат инверсии. Отсюда:
M = 2ᴸ − 1 − N.
Для нашего примера N = 22, L = 5, M = 31 − 22 = 9. И действительно, 01001₂ = 9.
Фиксированная длина имеет значение. Число 5 можно записать как 101. Инверсия этих трёх цифр — 010, результат равен 2. Если условие требует сначала представить 5 восемью двоичными цифрами, 00000101, то инверсия — 11111010, и результат 255 − 5 = 250.
Поэтому нельзя автоматически представлять каждое число восемью разрядами. Такая длина должна быть задана условием.
Аналогичный принцип работает в троичной системе при замене 0 ↔ 2, 1 → 1. Каждая исходная цифра d заменяется на 2 − d. Сумма двух записей состоит из двоек: N + M = 222…2₃ = 3ᴸ − 1. Следовательно, M = 3ᴸ − 1 − N.
В общем случае, если в системе с основанием q каждую цифру d заменяют на q − 1 − d, то M = qᴸ − 1 − N. Эта формула не относится к любой произвольной замене цифр. Она работает именно для указанного правила.
Одновременная замена
Если символы меняют одновременно, уже заменённые нельзя заменять ещё раз. Пусть нужно поменять местами нули и единицы. Кажется, что можно написать s.replace('0', '1').replace('1', '0'). Но это ошибка.
Метод создаёт строку с указанными заменами. В цепочке второй вызов работает с результатом первого. Для строки '10' получится 10 → 11 → 00, а правильная инверсия — 10 → 01.
s = '10110'print(s.replace('0', '1').replace('1', '0'))После первой замены в строке одни единицы, и вторая замена превращает их все в нули.
Надёжный способ — читать исходную строку и собирать новую: на каждом шаге анализируется символ исходной строки, поэтому уже заменённые символы больше не трогаются. Запусти программу по шагам и следи, как растёт new_s:
s = '10110'new_s = ''for digit in s: if digit == '0': new_s = new_s + '1' else: new_s = new_s + '0'print(new_s)Ведущие нули
Ведущие нули не меняют число, но могут изменить следующие действия со строкой. Python корректно прочитает int('0011', 2) и получит 3. Поэтому перед окончательным переводом специально удалять ведущие нули обычно не нужно.
Но рассмотрим два последовательных шага: получить строку 0011, затем дописать слева 1. Если сохранить строку, 0011 → 10011, то результат равен 19. Если самовольно убрать нули между шагами, 0011 → 11 → 111, то результат равен 7. Алгоритмы получились разными.
Удаляй ведущие нули в тот момент, когда этого требует условие, либо при окончательном переходе от записи к числу, если промежуточных действий больше нет.
Также помни: строка '0' — корректная запись нуля, а пустая строка '' — не запись числа, и int('', 2) вызовет ошибку. Если операция удаляет все символы, нужно проверить, допускает ли условие такой случай и как он трактуется.
Чётность и сумма цифр
Чётность самого числа видна по последней двоичной цифре, а сумма цифр зависит от всей записи. Чётное число делится на 2 без остатка. Для двоичной записи чётность самого числа определяется последней цифрой: 0 — число чётное, 1 — нечётное.
Но сумма двоичных цифр зависит от всей записи:
| N | Двоичная запись | Чётность N | Сумма двоичных цифр |
|---|---|---|---|
| 3 | 11 | Нечётное | 2 — чётная |
| 8 | 1000 | Чётное | 1 — нечётная |
| 6 | 110 | Чётное | 2 — чётная |
| 7 | 111 | Нечётное | 3 — нечётная |
Из таблицы видно: эти свойства могут совпадать, а могут различаться.
В двоичной записи сумма цифр равна количеству единиц: 1 + 1 + 0 + 1 = 3. Нули ничего не добавляют, каждая единица добавляет 1. Поэтому условия «сумма двоичных цифр чётная» и «количество единиц в двоичной записи чётное» равносильны. Но они не равносильны условию «N чётное».
При чтении условия каждый раз уточняй, чётность чего проверяют: числа, количества единиц, суммы цифр или длины записи.
Какая запись
Сумму цифр считают в той системе, которую называет условие. Для двоичной строки s.count('1') — одновременно количество единиц и сумма цифр. Для троичной строки — s.count('1') + 2 * s.count('2'). Более общий вариант для систем с цифрами 0–9 проходит по строке циклом : он по очереди подставляет в переменную digit каждый символ строки.
s = to_base(23, 3)print(s, s.count('1') + 2 * s.count('2')) total = 0for digit in s: total = total + int(digit)print(total)Здесь 23 = 212₃. Цикл прибавляет 2, затем 1, затем 2. Преобразование int(digit) нужно потому, что символ '2' — текст, а для сложения требуется число 2.
Тонкости:чётность и делимость в других системах
В троичной записи 212 количество единиц — 1, количество двоек — 2, а сумма цифр 2 + 1 + 2 = 5. Поэтому считать только единицы уже недостаточно. Сумму троичных цифр можно получить так: количество единиц + 2 · количество двоек.
Также нельзя автоматически переносить привычные признаки делимости из десятичной системы. Например, 3₁₀ = 10₃: само число делится на 3, но сумма цифр его троичной записи равна 1 и на 3 не делится. И наоборот, 5₁₀ = 12₃: сумма троичных цифр равна 3, но число 5 на 3 не делится.
В системе с основанием 3 делимость самого числа на 3 определяется последней цифрой, поскольку N = 3Q + d.
Для чётности в троичной системе есть другая полезная связь: все степени тройки нечётные. Поэтому чётность числа совпадает с чётностью суммы его троичных цифр. Например, 11₃ = 4: последняя цифра — 1, но число чётное, поскольку сумма цифр 1 + 1 = 2.
Отсюда полезное правило: в системе с чётным основанием чётность числа определяется последней цифрой, в системе с нечётным основанием — чётностью суммы цифр. Для задания чаще достаточно вычислить нужную величину непосредственно, но понимание этой связи помогает проверять себя.
Повторный шаг
Если шаг повторяют, второй раз его применяют уже к новой записи. Рассмотрим важный алгоритм:
- строится двоичная запись N;
- вычисляется сумма её цифр;
- остаток от деления суммы на 2 дописывается справа;
- для получившейся записи то же действие выполняется ещё раз.
Возьмём N = 13, s = 1101. Первая сумма 1 + 1 + 0 + 1 = 3. Остаток от деления 3 на 2 равен 1: 1101 → 11011. Теперь считаем сумму новой записи: 1 + 1 + 0 + 1 + 1 = 4. Остаток равен 0: 11011 → 110110. Получаем R = 110110₂ = 54.
Почему второй дописанный разряд всегда равен нулю? Возможны два случая. Если единиц было чётное количество, первым дописывается 0 и количество остаётся чётным. Если нечётное, первым дописывается 1 и количество становится чётным. В обоих случаях перед вторым шагом единиц чётное количество, и второй разряд — 0.
Следовательно, итоговый хвост — 00, если исходное количество единиц чётное, и 10, если нечётное. Отсюда:
R = 4N, если количество единиц чётное; R = 4N + 2, если нечётное.
Это свойство именно последовательного пересчёта. Если алгоритм говорит «найти остаток один раз и дописать его дважды», результаты другие: 00 или 11. Различие всего в нескольких словах условия, а числа на выходе меняются. Запусти обе версии по шагам и сравни, сколько раз считается count:
def parity_twice(n): s = bin(n)[2:] s = s + str(s.count('1') % 2) s = s + str(s.count('1') % 2) return int(s, 2) def parity_copied(n): s = bin(n)[2:] bit = str(s.count('1') % 2) return int(s + bit + bit, 2) print(parity_twice(13), parity_copied(13))В parity_twice второй count считает единицы уже новой строки — это и есть пересчёт. В parity_copied остаток посчитан один раз, поэтому к 13 дописывается 11, и R = 4 · 13 + 3 = 55.
Десятичная запись
Десятичную запись обрабатывают так же, как двоичную, — через строку. Сумма десятичных цифр числа — это сумма цифр строки str(n). В задачах с десятичной записью могут рассматриваться и все соседние пары или тройки цифр.
Пусть N = 30042. Все тройки соседних цифр: 300, 004, 042. Они перекрываются: после каждой тройки начало сдвигается на одну позицию. Как числа это 300, 4, 42. Если результат — разность максимального и минимального значения, R = 300 − 4 = 296. В программе по шагам видно, как i пробегает начала троек, а список values пополняется:
def transform_windows(n): s = str(n) values = [] for i in range(len(s) - 2): part = s[i:i + 3] values.append(int(part)) return max(values) - min(values) print(transform_windows(30042))Здесь values = [] создаёт пустой , append добавляет в его конец новое значение, а находят наибольшее и наименьшее. Функция предполагает, что в записи не меньше трёх цифр.
Почему range(len(s) - 2)? При длине 5 тройка может начинаться с индекса 0, 1 или 2. Начать с индекса 3 нельзя: останется только два символа. В общем случае для фрагментов длины k число возможных начал равно L − k + 1. Поэтому используют range(len(s) - k + 1).
Перед числовым сравнением фрагменты нужно превратить в числа. Например, строки '11' и '100' сравниваются посимвольно. Это не то же самое, что сравнение чисел 11 и 100 или двоичных чисел 11₂ и 100₂.
Итого
- Срез
s[a:b]— от a до b − 1;s[-2:]— две последние - Заменасрез без старых цифр плюс новый блок
- Переворот и инверсияразные действия; длину записи для инверсии задаёт условие
- Одновременная заменапо исходной строке, а не цепочкой replace
- Сумма цифрпо той записи, которую называет условие; при повторном шаге — по новой
Проверьте себя
Ответь сначала сам, потом открой ответ.
s = '1101001' запиши s[2:5], s[-3:] и s[::-1].ОтветСкрыть ответ
'010', '001' и '1001011'.'1100'.replace('0', '1').replace('1', '0') и почему это не инверсия?ОтветСкрыть ответ
'0000': первая замена делает все цифры единицами, вторая — все нулями.ОтветСкрыть ответ
10011. Какое число получится?ОтветСкрыть ответ
01100 = 8 + 4. Проверка: 19 + 12 = 31 = 11111.1100 дважды подряд допиши справа остаток от деления суммы цифр текущей записи на 2. Чему равно R?ОтветСкрыть ответ
11000; единиц снова две — ещё 0, 110000 = 48.Практика
Для проверки ответов нужен JavaScript. Условия, подсказки и решения доступны ниже.
Копия трёх последних цифр
Последние три двоичные цифры числа 45 скопировали в конец его записи. Какое число получилось?
Введите число.
Подсказка
Последние три двоичные цифры — остаток от деления на 8.
Решение
45 = 101101₂, последние три цифры 101 = 5, R = 45 · 8 + 5.
Решение программой
s = bin(45)[2:]print(int(s + s[-3:], 2))Ответ: 365
Заменить первые две цифры
В записи 101101 первые две цифры заменили на 110. Найди значение полученной двоичной записи.
Введите число.
Подсказка
Старый фрагмент удаляется срезом s[2:], новый ставится слева.
Решение
Первые две цифры 10 удаляются, остаётся 1101, слева встаёт 110: получается 1101101 = 64 + 32 + 8 + 4 + 1 = 109.
Решение программой
s = '101101'print(int('110' + s[2:], 2))Ответ: 109
Последнюю цифру заменить на 00
Последнюю цифру двоичной записи числа 23 заменили на 00. Какое число получилось?
Введите число.
Подсказка
Удалить последнюю цифру — N // 2, дописать 00 — умножить на 4.
Решение
23 = 10111₂ → 1011 + 00 = 101100₂, то есть 23 // 2 · 4 + 0.
Решение программой
s = bin(23)[2:]print(int(s[:-1] + '00', 2))Ответ: 44
Перенос двух цифр и инверсия
На вход алгоритма подаётся натуральное число N ≥ 4. Строится двоичная запись N. Две первые цифры записи переносятся в её конец в том же порядке, затем каждая цифра полученной записи заменяется на противоположную. Получившаяся запись (ведущие нули отбрасываются) — двоичная запись числа R. Укажи наименьшее N, для которого R больше 40.
Введите число.
Подсказка
Результат не растёт вместе с N: проверь и длину записи, и её вид.
Решение
32 = 100000₂ → 000010 → 111101₂ = 61. Пятизначные записи (N от 16 до 31) дают не больше 29, меньшие N — ещё меньше.
Решение программой
n = 4while True: s = bin(n)[2:] s = s[2:] + s[:2] t = '' for c in s: t += '1' if c == '0' else '0' if int(t, 2) > 40: print(n) break n += 1Ответ: 32
Инверсия записи 22
Инвертируй двоичную запись числа 22 без предварительного дополнения нулями. Какое число получилось?
Введите число.
Подсказка
Инверсия пятизначной записи — 31 − N.
Решение
22 = 10110, после инверсии 01001 = 9. Проверка: в каждом разряде одна единица, 22 + 9 = 31 = 11111.
Решение программой
s = bin(22)[2:]t = ''for c in s: t += '1' if c == '0' else '0'print(int(t, 2))Ответ: 9
Инверсия короткой и длинной записи
Сравни результаты инверсии числа 5: в его обычной двоичной записи и в записи длиной 8 двоичных цифр.
Введите два числа через пробел в том порядке, в каком их спрашивают.
Подсказка
Инверсия записи длины L — (2ᴸ − 1) − N.
Решение
Трёхзначная запись 101 после инверсии — 010 = 2, а восьмизначная 00000101 — 11111010 = 250.
Решение программой
def invert(s): t = '' for c in s: t += '1' if c == '0' else '0' return t s = bin(5)[2:]print(int(invert(s), 2), int(invert(s.zfill(8)), 2))Ответ: 2 250
Переворот записи 52
Двоичную запись числа 52 перевернули и удалили ведущие нули. Какое число получилось?
Введите число.
Подсказка
После переворота нули оказываются впереди и не влияют на число.
Решение
52 = 110100, перевёрнутая запись 001011; ведущие нули не меняют число: 1011 = 11.
Решение программой
print(int(bin(52)[2:][::-1], 2))Ответ: 11
Обмен цифр 0 и 2
В троичной записи числа 46 каждую цифру 0 заменили на 2, а 2 — на 0. Какое число получилось? Проверь ответ формулой.
Введите число.
Подсказка
Меняй цифры за один проход, иначе вторая замена испортит первую.
Решение
46 = 1201₃ → 1021₃ = 27 + 6 + 1; по формуле 3⁴ − 1 − 46 = 80 − 46.
Решение программой
def to_base(n, q): if n == 0: return '0' s = '' x = n while x > 0: digit = x % q s = str(digit) + s x = x // q return s swap = {'0': '2', '1': '1', '2': '0'}t = ''for c in to_base(46, 3): t += swap[c]print(int(t, 3))Ответ: 34
Дважды дописать чётность
Для N = 7 дважды последовательно допиши справа остаток от деления суммы текущих двоичных цифр на 2. Найди результат.
Введите число.
Подсказка
Второй шаг считает единицы уже в новой записи.
Решение
7 = 111: единиц три — дописываем 1, получаем 1111; теперь единиц четыре — дописываем 0: 11110 = 30.
Решение программой
s = bin(7)[2:]for _ in range(2): s += str(s.count('1') % 2)print(int(s, 2))Ответ: 30
Трижды дописать чётность, R > 300
На вход алгоритма подаётся натуральное число N. Строится двоичная запись N. Затем три раза подряд к текущей записи справа дописывается остаток от деления суммы её цифр на 2. Полученная запись — двоичная запись числа R. Укажи наименьшее N, для которого R больше 300.
Введите число.
Подсказка
Хвост из трёх цифр — 000 или 100, R примерно 8N.
Решение
38 = 100110₂, три единицы, хвост 100, R = 8 · 38 + 4 = 308. При N = 37 получается ровно 300.
Решение программой
n = 1while True: s = bin(n)[2:] for _ in range(3): s += str(s.count('1') % 2) if int(s, 2) > 300: print(n) break n += 1Ответ: 38
Сумма цифр в двух системах
Для N = 100 найди сумму цифр десятичной записи и сумму цифр двоичной записи.
Введите два числа через пробел в том порядке, в каком их спрашивают.
Подсказка
Сумма двоичных цифр — количество единиц.
Решение
Сумма десятичных цифр 100: 1 + 0 + 0 = 1. Двоичная запись 100 = 1100100, в ней три единицы — сумма 3.
Решение программой
digits = 0for c in str(100): digits += int(c)print(digits, bin(100).count('1'))Ответ: 1 3
Пары соседних цифр
На вход алгоритма подаётся натуральное число N > 1000. Все пары соседних цифр десятичной записи N рассматриваются как двузначные числа (возможно, с ведущим нулём). Результат R — сумма наибольшего и наименьшего из этих чисел. Укажи наименьшее N, для которого R = 100.
Введите число.
Подсказка
Пара с ведущим нулём, например 09, — это число 9.
Решение
Для 1091 пары 10, 09 и 91 — числа 10, 9 и 91; наибольшее плюс наименьшее: 91 + 9 = 100. Меньшие N > 1000 так не дают — это проверяет программа.
Решение программой
n = 1001while True: s = str(n) pairs = [] for i in range(len(s) - 1): pairs.append(int(s[i:i + 2])) if max(pairs) + min(pairs) == 100: print(n) break n += 1Ответ: 1091