1 ИНТЕРАКТИВНЫЙ ИНТЕРПРЕТАТОР ......................................................................................... 3 2 ТИПЫ ДАННЫХ .................................................................................................................................... 4 2.1 2.2 2.3 2.4 2.5 ОПРЕДЕЛЕНИЕ ТИПА ОБЪЕКТА ............................................................................................................. 4 ОСНОВНЫЕ ОПЕРАЦИИ ДЛЯ ЧИСЕЛ ....................................................................................................... 4 ПРИОРИТЕТЫ АРИФМЕТИЧЕСКИХ ОПЕРАЦИЙ ....................................................................................... 5 ОСНОВНЫЕ ОПЕРАЦИИ НАД СТРОКАМИ: .............................................................................................. 5 ПРЕОБРАЗОВАНИЕ ТИПОВ ..................................................................................................................... 5 3 ВВОД ДАННЫХ: ФУНКЦИЯ INPUT() .............................................................................................. 6 4 ВЫВОД ДАННЫХ: ФУНКЦИЯ PRINT() .......................................................................................... 6 5 ЦЕЛОЧИСЛЕННАЯ АРИФМЕТИКА ............................................................................................... 7 5.1 ПРИМЕРЫ ЗАДАЧ ................................................................................................................................... 7 5.1.1 Задача «Дележ яблок» ................................................................................................................ 7 5.1.2 Задача «МКАД» ........................................................................................................................... 7 УСЛОВНАЯ ИНСТРУКЦИЯ ............................................................................................................... 8 6 6.1 ПРИМЕР................................................................................................................................................. 8 6.2 ВЛОЖЕННЫЕ УСЛОВНЫЕ ИНСТРУКЦИИ ................................................................................................ 9 6.3 ПРИМЕРЫ ЗАДАЧ ................................................................................................................................... 9 6.3.1 Задача «Упорядочить три числа» ............................................................................................. 9 6.3.2 Задача «Существует ли треугольник?» ................................................................................. 10 6.3.3 Задача «Сколько совпадает чисел» ......................................................................................... 10 6.3.4 Задача «Ход короля» ................................................................................................................. 11 6.3.5 Задача «Ход ферзя» ................................................................................................................... 12 6.3.6 Задача «Шахматная доска» .................................................................................................... 12 6.3.7 Задача «Шоколадка» ................................................................................................................. 13 6.3.8 Задача «Линейное уравнение» .................................................................................................. 13 6.3.9 Задача «Коровы» ....................................................................................................................... 14 6.3.10 Задача «Тестирующая система» ......................................................................................... 15 7 ТИП ДАННЫХ BOOL ......................................................................................................................... 16 8 ЦИКЛ FOR ............................................................................................................................................. 17 8.1 ФУНКЦИЯ RANGE ................................................................................................................................ 17 8.2 РАЗБОР ЗАДАЧ ..................................................................................................................................... 18 8.2.1 Задача «Ряд - 2» ......................................................................................................................... 18 8.2.2 Задача «Ряд - 3» ......................................................................................................................... 18 8.2.3 Задача «Сумма кубов» .............................................................................................................. 19 8.2.4 Задача «Факториал» ................................................................................................................. 19 8.2.5 Задача «Фибоначчиева последовательность» ....................................................................... 19 8.2.6 Задача «Флаги» .......................................................................................................................... 20 8.2.7 Задача «Чётные числа» ............................................................................................................ 21 8.2.8 Задача «Количество нулей» ...................................................................................................... 21 8.2.9 Задача «Диофантово уравнение - 2» ....................................................................................... 22 8.2.10 Задача «Потерянная карточка» .......................................................................................... 23 ЦИКЛ WHILE ....................................................................................................................................... 24 9 10 СТРОКИ .............................................................................................................................................. 25 10.1 СРЕЗЫ (SPLICES) .............................................................................................................................. 25 10.2 МЕТОДЫ .......................................................................................................................................... 26 10.2.1 Методы find и rfind .............................................................................................................. 26 2 10.2.2 Методы replace ................................................................................................................... 26 10.2.3 Методы count .................................................................................................................... 26 10.3 РАЗБОР ЗАДАЧ.................................................................................................................................. 26 10.3.1 Задача «Делаем срезы» .......................................................................................................... 26 10.3.2 Задача «Две половинки» ......................................................................................................... 27 10.3.3 Задача «Переставить два слова»......................................................................................... 28 10.3.4 Задача «Второе вхождение» ................................................................................................ 28 10.3.5 Задача «Количество слов» .................................................................................................... 29 10.3.6 Задача «Обращение фрагмента» ......................................................................................... 29 10.3.7 Задача «Замена внутри фрагмента» ................................................................................... 29 10.3.8 Задача «Метод бутерброда»................................................................................................ 30 10.3.9 Задача «Благозвучное слово» ................................................................................................ 30 10.3.10 Задача «Телефонные номера» ............................................................................................... 31 11 МАССИВЫ (СПИСКИ).................................................................................................................... 32 11.1 11.2 11.3 11.4 11.5 11.6 12 ФУНКЦИИ.......................................................................................................................................... 38 12.1 13 ОСНОВНЫЕ СВЕДЕНИЯ .................................................................................................................... 33 ВВОД (ФОРМИРОВАНИЕ) ЭЛЕМЕНТОВ СПИСКА ............................................................................... 33 ВВОД-ВЫВОД: МЕТОДЫ SPLIT И JOIN ............................................................................................... 34 СРЕЗЫ (SPLICES) .............................................................................................................................. 34 ОПЕРАЦИИ СО СПИСКАМИ .............................................................................................................. 35 ОБРАБОТКА И ВЫВОД ВЛОЖЕННЫХ СПИСКОВ ................................................................................ 35 РЕКУРСИЯ ........................................................................................................................................ 40 ФАЙЛОВЫЙ ВВОД-ВЫВОД ......................................................................................................... 41 13.1 ОТКРЫТИЕ ФАЙЛА ........................................................................................................................... 41 14 МНОЖЕСТВА.................................................................................................................................... 42 15 СЛОВАРИ ........................................................................................................................................... 43 15.1 ПЕРЕБОР ЭЛЕМЕНТОВ СЛОВАРЯ ...................................................................................................... 46 3 1 Интерактивный интерпретатор Данный курс будет посвящен изучению программирования с использованием языка Python. Это — современный язык программирования, работающий на всех распространных операционных системах. В настоящее время существует две версии языка Python: более старая, но пока ещё более распространненая версия 2 и современная версия 3. Они не вполне совместимы друг с другом: программа, написанная для одной версии языка может оказаться невыполнимой для другой версии. Но в основном обе версии очень похожи. Мы будем использовать версию 3 данного языка, именно её необходимо установить дома, скачав данную версию с сайта www.python.org. Этот модуль вызывает окно отладки (можно создать на него ярлык) Строка приглашения (начало ввода команд) Смело вводите команды и наслаждайтесь результатом. А что можно вводить? Несколько примеров: >>> 2 + 2 команда вычисляет сумму двух чисел 4 >>> 2 ** 100 1267650600228229401496703205376 >>> 'Hello' + 'World' 'HelloWorld' >>> 'ABC' * 100 'ABCABCABCABCABCABCABCABCABCABC ABCABCABCABCABCABCABCABCABCABCABC ABCABCABCABCABCABCABCABCABCABCABC ABCABCABCABCABCABCABCABCABCABC ABCABCABCABCABCABCABCABCABCABCABC ABCABCABCABCABCABCABCABCABCABC команда вычисляет 2 в степени 100 команда выполняет операцию конкатенации для строк команда печатает строку 'ABC', повторенную 100 раз ABCABCABCABCABCABCABCABCABC ABCABCABCABCABCABCABCABCABCABCABCABC ABCABCABCABCABCABCABCABCABCABCABC ABCABCABCABCABC' команду exit() или нажмите Ctrl+D закончить работу с питоном Создание файла, вызов, редактирование, выполнение 4 2 Типы данных Итак, мы видим, что Питон умеет работать как минимум с двумя видами данных — числами и строками. Числа записываются последовательностью цифр, также перед числом может стоять знак минус, а строки записываются в одинарных кавычках. 2 и '2' — это разные объекты, первый объект — число, а второй —строка. Операция + для целых чисел и для строк работает по-разному: для чисел это сложение, а для строк —конкатенация. Кроме целых чисел есть и другой класс чисел: действительные (вещественные числа), представляемые в виде десятичных дробей. Они записываются с использованием десятичной точки, например, 2.0. В каком-то смысле, 2 и 2.0 имеют равные значение, но это—разные объекты. Например, можно вычислить значения выражения 'ABC' * 10 (повторить строку 10 раз), но нельзя вычислить 'ABC' * 10.0. 2.1 Определение типа объекта можно при помощи функции type: >>> type(2) <class 'int'> >>> type('2') <class 'str'> >>> type(2.0) <class 'float'> 2.2 Обратите внимание —type является функцией, аргументы функции указываются в скобках после ее имени. Основные операции для чисел A+B A-B A*B A/B A ** B — сумма; — разность; — произведение; — частное; — возведение в степень. унарный вариант операции с одним аргументом Например: -A Полезно помнить, что квадратный корень из числа x — это x ** 0.5, а корень степени n это x ** (1 / n). возвращает число, противоположное данному 5 2.3 Приоритеты арифметических операций унарный минус (-5+as) возведение в степень выполняются справа налево: умножение, деление сложение, вычитание деление / (результат – вещественный – float) Основные операции над строками: A + B — конкатенация; A * n — повторение n раз, значение n должно быть целого типа. 2.5 Преобразование типов Иногда бывает полезно целое число записать, как строку. И, наоборот, если строка состоит из цифр, то полезно эту строку представить в виде числа, чтобы дальше можно было выполнять арифметические операции с ней. Для этого используются функции, одноименные с именем типа, то есть int, float, str. Например, int('123') вернет целое число 123, а str(123) вернет строку '123'. Пример: 2.4 >>> str(2 + 2) * int('2' + '2') '4444444444444444444444' Результатом будет строка из числа 4, повторенная 22 раза. Пишем простейшие программы В предыдущем задании мы использовали Питон для простых разовых вычислений, используя интерактивный режим. Например, было задание вычислить длину гипотенузы прямоугольного треугольника по ее катетам. Запустите текстовый редактор и напишите следующий текст: a = 179 b = 197 c = (a ** 2 + b ** 2) ** 0.5 print (c) 6 3 Ввод данных: функция input() a = input() b = input() Функция input возвращает текстовую строку, а нам нужно сделать так, чтобы переменные имели целочисленные значения. Поэтому сразу же после считывания выполним преобразование типов при помощи a = int(a) b = int(b) a = int(input()) b = int(input()) с=a*b Можно объединить считывание строк и преобразование типов, если вызывать функцию int для того значения, которое вернет функция input: фунцкии int, и запишем новые значения в переменные a и b. 4 a=1 b=2 print(a, '+', b, '=', a + b) print(a, b, a + b,sep=’:’) print(a, b, a + b,sep=’’) print(a, b, a + b) Вывод данных: функция print() Функция print может выводить не только значения переменных, но и значения любых выражений. Параметр sep Обратите внимание, выводимые значение разделяются одним пробелом. Но такое поведение можно изменить: можно разделять выводимые значения двумя пробелами, любым другим символом, любой другой строкой, выводить их в отдельных строках или не разделять никак. Для этого нужно функции print передать специальный именованный параметр, называемый sep, равный строке, используемый в качестве разделителя (sep — аббревиатура от слова separator, т.е. разделитель). По умолчанию параметр sep равен строке из одного пробела и между значениями выводится пробел. Чтобы использовать в качестве разделителя, например, символ двоеточия нужно передать параметр sep, равный строке ':' print(a, b, sep = '\n') Вывод с новой строки каждого параметра А для того, чтобы вставить в строку сам символ обратного слэша, нужно повторить его два раза: '\\'. Переход на новую строку после списка вывода(по умолчанию) Параметр end print(a, b, c, sep = '', end = '\n') a=1 b=2 print(a) print(b) a=1 b=2 print(a, b, end = '') print('******=',a+b) 7 5 Целочисленная арифметика Для целых чисел определены ранее рассматривавшиеся операции +, -, * и **. Операция деления / для целых чисел возвращает значение типа float. Также функция возведения в степень возвращает значение типа float, если показатель степени — отрицательное число. Но есть и специальная операция целочисленного деления, выполняющегося с отбрасыванием дробной части, которая обозначается //. Она возвращает целое число: целую часть частного. Например: >>> 17 // 3 5 >>> -17 // 3 -6 Другая близкая ей операция: это операция взятия остатка от деления, обозначаемая %: >>> 17 % 3 2 >>> -17 % 3 1 5.1 Примеры задач 5.1.1 Задача «Дележ яблок» n школьников делят k яблок поровну, неделящийся остаток остается в корзинке. Сколько яблок достанется каждому школьнику? Входные данные Программа получает на вход числа n и k - целые, положительные, не превышают 10000. Выходные данные Выведите ответ на задачу. Примеры входные данные 3 14 выходные данные 4 Разбор задачи «Дележ яблок» В этой задаче надо просто найти целую часть от деления, используя операцию деления нацело в Pytrhon. Решение: n = int(input()) k = int(input()) print(k//n) 5.1.2 Задача «МКАД» Длина Московской кольцевой автомобильной дороги —109 километров. Байкер Вася стартует с нулевого километра МКАД и едет со скоростью v километров в час. На какой отметке он остановится через t часов? Входные данные Программа получает на вход значение v и t. Если v>0, то Вася движется в положительном направлении по МКАД, если же значение v<0, то в отрицательном. (Гарантируется, что исходные числа - целые и находятся в промежутке от -1000 до +1000). Выходные данные Программа должна вывести целое число от 0 до 108 — номер отметки, на которой остановится Вася. Примеры входные данные 60 2 выходные данные 8 11 входные данные -1 1 выходные данные 108 Разбор задачи «МКАД» В этой задаче надо взять остаток от деления на 109. Решение: v = int(input()) t = int(input()) print((v*t)%109) 6 Условная инструкция Все ранее рассматриваемые программы имели линейную структуру: все инструкции выполнялись последовательно одна за одной, каждая записанная инструкция обязательно выполняется. Допустим мы хотим по данному числу x определить его абсолютную величину (модуль). Программа должна напечатать значение переменной x, если x>0 или же величину -x в противном случае. Линейная структура программы нарушается: в зависимости от справедливости условия x>0 должна быть выведена одна или другая величина. 6.1 Пример x = int(input()) if x > 0: print(x) else: print(-x) В этой программе используется условная инструкция if (если). После слова if указывается проверяемое условие (x > 0), завершающееся двоеточием. После этого идет блок (последовательность) инструкций, который будет выполнен, если условие истинно, в нашем примере это вывод на экран величины x. Затем идет слово else (иначе), также завершающееся двоеточием, и блок инструкций, который будет выполнен, если проверяемое условие неверно, в данном случае будет выведено значение -x. Итак, условная инструкция в Питоне имеет следующий синтаксис: if Условие: Блок инструкций 1 else: Блок инструкций 2 Блок инструкций 1 будет выполнен, если Условие истинно. Если Условие ложно, будет выполнен Блок инструкций 2. В условной инструкции может отсутствовать слово else и последующий блок. Такая инструкция называется неполным ветвлением. Например, если дано число x и мы хотим заменить его на абсолютную величину x, то это можно сделать следующим образом: if x < 0: x = -x print(x) В этом примере переменной x будет присвоено значение -x, но только в том случае, когда x<0. А вот инструкция print(x) будет выполнена всегда, независимо от проверяемого условия. Для выделения блока инструкций, относящихся к инструкции if или else в языке Питон используются отступы. Все инструкции, которые относятся к одному блоку, должны иметь равную величину отступа, то есть одинаковое число пробелов в начале строки. Рекомендуется использовать отступ в 4 пробела и 9 не рекомедуется использовать в качестве отступа символ табуляции. 6.2 Вложенные условные инструкции Внутри условных инструкций можно использовать любые инструкции языка Питон, в том числе и условную инструкцию. Получаем вложенное ветвление – после одной развилки в ходе исполнения программы появляется другая развилка. При этом вложенные блоки имеют больший размер отступа (например, 8 пробелов). Покажем это на примере программы, которая по данным ненулевым числам x и y определяет, в какой из четвертей координатной плоскости находится точка (x,y): x = int(input()) y = int(input()) if x > 0: if y > 0: # x>0, y>0 print("Первая четверть") else: # x>0, y<0 print("Четвертая четверть") else: if y > 0: # x<0, y>0 print("Вторая четверть") else: # x<0, y<0 print("Третья четверть") В этом примере мы использовали комментарии – текст, который интерпретатор игнорирует. Комментариями в Питоне является символ # и весь текст после этого символа до конца строки. Операторы сравнения Как правило, в качестве проверяемого условия используется результат вычисления одного из следующих операторов сравнения: < Меньше — условие верно, если первый операнд меньше второго. > Больше — условие верно, если первый операнд больше второго. <= Меньше или равно. >= Больше или равно. == Равенство. Условие верно, если два операнда равны. != Неравенство. Условие верно, если два операнда неравны. Например, условие (x * x < 1000) означает “значение x * x меньше 1000”, а условие (2 * x != y) означает “удвоенное значение переменной x не равно значению переменной y”. Операторы сравнения в Питоне можно объединять в цепочки (в отличии от большинства других языков программирования, где для этого нужно использовать логические связки), например, a == b == c или 1 <= x <= 10. 6.3 Примеры задач 6.3.1 Задача «Упорядочить три числа» Дано три числа. Упорядочите их в порядке неубывания. Программа должна считывать три числа a, b, c, затем программа должна менять их значения так, чтобы стали выполнены условия a <= b <= c, затем программа выводит тройку a, b, c. Входные данные Вводятся три числа. Выходные данные Выведите ответ на задачу. Примечание Дополнительные ограничения: нельзя использовать дополнительные переменные (то есть единственной допустимой операцией присваивания является обмен значений двух переменных типа (a, b) = (b, a). Примеры входные данные 1 2 10 1 выходные данные 112 Рекомендации к задаче При решении задачи можно использовать упрощенный обмен значений двух переменных, имеющийся в языке Python: a, b = b, a Разбор задачи «Упорядочить три числа» При решении задачи возникало 2 проблемы: Переменные не менялись местами, но при этом их значения выводились в правильном порядке; Было выполнено слишком много сравнений, что является неоптимальным решением. Для решения задачи достаточно было за 2 сравнения поставить на свое место самое большое значение, и еще одним сравнением упорядочить остальные 2 значения. Пример программы представлен ниже: a = int(input()) b = int(input()) c = int(input()) if a > b : a, b = b, a if b > c : b, c = c, b if a > b : a, b = b, a print(a, b, c) 6.3.2 Задача «Существует ли треугольник?» Даны три натуральных числа a, b, c. Определите, существует ли треугольник с такими сторонами. Если треугольник существует, выведите строку YES, иначе выведите строку NO. Треугольник — это три точки, не лежащие на одной прямой. Входные данные Вводятся три натуральных числа. Выходные данные Выведите ответ на задачу. Примеры входные данные 3 4 5 выходные данные YES Разбор задачи «Существует ли треугольник?» Для решения данной задачи необходимо было вспомнить, что треугольник существует только тогда, когда сумма любых двух его сторон больше третьей: a = int(input()) b = int(input()) c = int(input()) if (a + b > c) and (b + c > a) and (a + c > b) : print( 'YES' ) else : print ( 'NO' ) Решение можно упростить, если найти максимальную из трех сторон и проверить, будет ли она меньше суммы двух других 6.3.3 Задача «Сколько совпадает чисел» Даны три целых числа. Определите, сколько среди них совпадающих. Программа должна вывести одно из чисел: 3 (если все совпадают), 2 (если два совпадает) или 0 (если все числа различны). Входные данные Вводятся три целых числа. 11 Выходные данные Выведите ответ на задачу. Примеры входные данные 1 2 3 выходные данные 0 Разбор задачи «Сколько совпадает чисел» В данной задаче достаточно отдельно рассмотреть случай, когда равны все 3 значения, когда равны только 2 из них и когда все значения различны: a = int(input()) b = int(input()) c = int(input()) if a == b == c: print('3') elif a == b or b == c or a == c: print('2') else: print('0') 6.3.4 Задача «Ход короля» Шахматный король ходит по горизонтали, вертикали и диагонали, но только на 1 клетку. Даны две различные клетки шахматной доски, определите, может ли король попасть с первой клетки на вторую одним ходом. Входные данные Программа получает на вход четыре числа от 1 до 8 каждое, задающие номер столбца и номер строки сначала для первой клетки, потом для второй клетки. Выходные данные Программа должна вывести YES, если из первой клетки ходом короля можно попасть во вторую или NO в противном случае. Примеры входные данные 4 4 5 5 выходные данные YES Разбор задачи «Ход короля» Король может пойти на любую соседнюю клетку, поэтому достаточно проверить, насколько изменились координаты по горизонтали и вертикали. Если каждая координата изменилась не более чем на 1 (не важно, в какую сторону), то ходом короля можно попасть из первой клетки во вторую. В противном случае, этого сделать нельзя: a = int(input()) b = int(input()) c = int(input()) d = int(input()) if abs(a-c) <= 1 and abs(b-d) <= 1: print('YES') else: print('NO') Это решение выдаст YES и для совпадающих клеток, но таких во входных данных быть не должно. 12 6.3.5 Задача «Ход ферзя» Программа получает на вход четыре числа от 1 до 8 каждое, задающие номер столбца и номер строки сначала для первой клетки, потом для второй клетки. Выходные данные Программа должна вывести YES, если из первой клетки ходом ферзя можно попасть во вторую или NO в противном случае. Примеры входные данные 1 1 2 2 выходные данные YES входные данные 1 1 2 3 выходные данные NO Разбор задачи «Ход ферзя» Ферзь может перейти из первой клетки во вторую, если он движется по вертикали (не изменилась координата столбца), по горизонтали (не изменилась координата строки) или по диагонали (координаты строки и столбца изменились на одну и ту же величину, не важно, в какую сторону): a=int(input()) b=int(input()) c=int(input()) d=int(input()) if abs(c-a)==abs(d-b) or a==c or b==d: print('YES') else: print('NO') 6.3.6 Задача «Шахматная доска» Заданы две клетки шахматной доски. Если они покрашены в один цвет, то выведите слово YES, а если в разные цвета – то NO. Входные данные Вводятся 4 числа - координаты клеток. Выходные данные Выведите ответ на задачу. Примеры входные данные 1 1 2 2 выходные данные YES входные данные 1 1 2 3 13 выходные данные NO Разбор задачи «Шахматная доска» Решение данной задачи основывается на том, что сумма координат клеток одного цвета обладает одной и той же четностью, то есть для одних клеток эта сумма четна, для клеток другого цвета – нечетна: a = int(input()) b = int(input()) c = int(input()) d = int(input()) if (a+b)%2==(c+d)%2: print('YES') else: print('NO') 6.3.7 Задача «Шоколадка» Шоколадка имеет вид прямоугольника, разделенного на n×m долек. Шоколадку можно один раз разломить по прямой на две части. Определите, можно ли таким образом отломить от шоколадки ровно k долек. Входные данные Программа получает на вход три числа: n, m, k Выходные данные Программа должна вывести одно из двух слов: YES или NO. Примеры входные данные 4 2 6 выходные данные YES входные данные 2 10 7 выходные данные NO Разбор задачи «Шоколадка» Ровно К долек можно отломить при условии, что количество требуемых долек не превышает общего числа долек в шоколадке и К кратно одной или ширине, или длине шоколадки: n=int(input()) m=int(input()) k=int(input()) if k<=n*m and (k%n==0 or k%m==0): print('YES') else: print('NO') 6.3.8 Задача «Линейное уравнение» Даны числа a и b. Решите в целых числах уравнение ax+b=0. Выведите все решения этого уравнения, если их число конечно, выведите словоNO, если решений нет, выведите слово INF, если решений бесконечно много. Входные данные Вводятся два числа. Выходные данные Выведите ответ на задачу. Примеры 14 входные данные 1 -7 выходные данные 7 входные данные 6 -2 выходные данные NO Рекомендации к задаче «Линейное уравнение» Сначала решите уравнение на бумаге в общем случае. Определите когда такое решение возможно и является целочисленным. Выпишите, во что вырождается уравнение, не подходящее под общий случай. Определите, когда полученное равенство имеет смысл. Составьте программу, в которой бы анализ каждого из параметров задачи производился не более одного раза. Разбор задачи «Линейное уравнение» По условию требовалось, чтобы анализ каждой переменной производился только один раз, если данное требование не выполнялось, решение было проигнорировано. Для начала можно решить общий случай, когда а≠0. Необходимо помнить, что требуется найти целочисленное решение, поэтому нужно проверить, делится ли b нацело на а. Далее нужно рассмотреть особый случай при a=0. При b=0 получаем бесконечное число решений, в противном случае решений нет. Ниже представлено решение задачи: a = int(input()) b = int(input()) if a!=0: if b%a==0: x=-b//a print(x) else: print("NO") else: if b==0: print("INF") else: print("NO") 6.3.9 Задача «Коровы» Для данного числа n<100 закончите фразу “На лугу пасется...” одним из возможных продолжений: “n коров”, “n корова”, “n коровы”, правильно склоняя слово “корова”. Входные данные Вводится натуральное число. Выходные данные Программа должна вывести введенное число n и одно из слов: korov, korova или korovy. Между числом и словом должен стоять ровно один пробел. Примеры входные данные 1 выходные данные 1 korova входные данные 2 выходные данные 2 korovy входные данные 5 15 выходные данные 5 korov Рекомендации к задаче «Коровы» В этой программе обязательно использовать конструкцию elif в случае решения задачи на языке Python. Разбор задачи «Коровы» По условию задачи требовалось обязательно использовать конструкцию elif, без которой решение игнорировалось. В задаче было необходимо аккуратно определить, при каких условиях склонять слово «корова». Заметим, что если количество оканчивается на 1 (например, 21, 31 и др.), то слово не склоняется. Если количество оканчивается на 2, 3 или 4 (22, 43, 34 и др.), то правильным будет вариант «коровы». В остальных случаях правильным будет слово «коров». Проверить последнюю цифру в числе можно, взяв остаток от деления числа на 10. Следует обратить внимание на то, что из описанных выше правил есть исключения: для значений, оканчивающихся на 11, 12, 13, 14 необходимо прописать отдельное условие: n = int(input()) if 10 <n%100 < 15: print(n, 'korov') elif n%10 == 1: print(n,'korova') elif 2 <= n%10 <= 4: print(n, 'korovy') else: print(n, 'korov') В заключение заметим, что также склоняются и другие слова русского языка (рубль, рубля, рублей и т.п.), то есть полученная закономерность является универсальной. 6.3.10 Задача «Тестирующая система» Денис Павлович задал школьникам задачу: “Если данное четырехзначное число является симметричным, выведите 1, иначе выведите любое другое целое число”. Для проверки Денис Павлович использует заранее подготовленный набор тестов и правильных ответов к ним. Ире кажется, что она решила эту задачу, но тестирующая система Ejudge почему-то не принимает ее решение. Ира думает, что это происходит оттого, что она выводит не то любое другое число, которое записано в ответах у Дениса Павловича. Напишите программу, которая по ответу Дениса Павловича и по ответу Иры определяет, верно ли Ира решила задачу. Входные данные Программа получает на вход два числа: ответ Дениса Павловича и ответ Иры. Выходные данные Программа должна вывести YES, если Ира дала верный ответ и NO в противном случае. Примеры входные данные 11 -1 выходные данные YES входные данные 3 1 выходные данные NO Рекомендации к задаче «Тестирующая система» В этой задаче главное – правильно понять ее условие, что в олимпиадных задачах по информатике является очень важным умением. Разбор задачи «Тестирующая система» 16 В данной задаче надо проверить совпадают ли числа. Совпадающие – это либо равные единице, либо не равные. a = int(input()) b = int(input()) if (a != 1 and b != 1) or (a == b == 1): print('YES') else: print('NO') 7 Тип данных bool Операторы сравнения возвращают значения специального логического типа bool. Значения логического типа могут принимать одно из двух значений: True (истина) или False (ложь). Если преобразовать логическое True к типу int, то получится 1, а преобразование False даст 0. При обратном преобразовании число 0 преобразуется в False, а любое ненулевое число в True. При преобразовании str в bool пустая строка преобразовывается в False, а любая непустая строка в True. Логические операторы Иногда нужно проверить одновременно не одно, а несколько условий. Например, проверить, является ли данное число четным можно при помощи условия (n % 2 == 0) (остаток от деления n на 2 равен 0), а если необходимо проверить, что два данных целых числа n и m являются четными, необходимо проверить справедливость обоих условий: n % 2 == 0 и m % 2 == 0, для чего их необходимо объединить при помощи оператора and (логическое И): n % 2 == 0 and m % 2 == 0. В Питоне существуют стандартные логические операторы: логическое И, логическое ИЛИ, логическое отрицание. Логическое И является бинарным оператором (то есть оператором с двумя операндами: левым и правым) и имеет вид and. Оператор and возвращает True тогда и только тогда, когда оба его операнда имеют значение True. Логическое ИЛИ является бинарным оператором и возвращает True тогда и только тогда, когда хотя бы один операнд равен True. Оператор “логическое ИЛИ” имеет вид or. Логическое НЕ (отрицание) является унарным (то есть с одним операндом) оператором и имеет вид not, за которым следует единственный операнд. Логическое НЕ возвращает True, если операнд равен False и наоборот. Пример. Проверим, что хотя бы одно из чисел a или b оканчивается на 0: if a % 10 == 0 or b % 10 == 0: Проверим, что число a — положительное, а b — неотрицательное: if a > 0 and not (b < 0): Или можно вместо not (b < 0) записать (b >= 0). Каскадные условные инструкции Пример программы, определяющий четверть координатной плоскости, можно переписать используя “каскадную“ последовательность операцией if... elif... else: x = int(input()) y = int(input()) if x > 0 and y > 0: print("Первая четверть") elif x > 0 and y < 0: print("Четвертая четверть") elif y > 0: print("Вторая четверть") else: print("Третья четверть") В такой конструкции условия if, ..., elif проверяются по очереди, выполняется блок, соответствующий первому из истинных условий. Если все проверяемые условия ложны, то выполняется блок else, если он присутствует. 17 8 Цикл for Цикл for, также называемый циклом с параметром, в языке Питон богат возможностями. В цикле for указывается переменная и множество значений, по которому будет пробегать переменная. Множество значений может быть задано списком, кортежем, строкой или диапазоном. Вот простейший пример использования цикла, где в качестве множества значений используется кортеж: i=1 for color in 'red', 'orange', 'yellow', 'green', 'cyan', 'blue', 'violet': print(i,'-th color of rainbow is ', color, sep = '') i += 1 В этом примере переменная color последовательно принимает значения 'red', 'orange' и т.д. В теле цикла выводится сообщение, которое содержит название цвета, то есть значение переменной color, а также номер итерации цикла число, которое сначала равно 1, а потом увеличивается на один (инструкцией i += 1 с каждым проходом цикла. В списке значений могут быть выражения различных типов, например: for i in 1, 2, 3, 'one', 'two', 'three': print(i) При первых трех итерациях цикла переменная i будет принимать значение типа int, при последующих трех — типа str. 8.1 Функция range Как правило, циклы for используются либо для повторения какой-либо последовательности действий заданное число раз, либо для изменения значения переменной в цикле от некоторого начального значения до некоторого конечного. Для повторения цикла некоторое заданное число раз n можно использовать цикл for вместе с функцией range: for i in range(n): Тело цикла В качестве n может использоваться числовая константа, переменная или произвольное арифметическое выражение (например, 2 ** 10). Если значение n равно нулю или отрицательное, то тело цикла не выполнится ни разу. Если задать цикл таким образом: for i in range(a, b): Тело цикла то индексная переменная i будеть принимать значения от a до b - 1, то есть первый параметр функции range, вызываемой с двумя параметрами, задает начальное значение индексной переменной, а второй параметр — значение, которая индексная переменная принимать не будет. Если же b≥a, то цикл не будет выполнен ни разу. Например, для того, чтобы просуммировать значения чисел от 1 до n можно воспользоваться следующей программой: sum = 0 for i in range(1, n + 1): sum += i В этом примере переменная i принимает значения 1, 2, ..., n, и значение переменной sum последовательно увеличивается на указанные значения. Наконец, чтобы организовать цикл, в котором индексная переменная будет уменьшаться, необходимо использовать функцию range с тремя параметрами. Первый параметр задает начальное значение индексной переменной, второй параметр — значение, до которого будет изменяться индексная переменная (не включая его!), а третий параметр — величину изменения индексной переменной. Например, сделать цикл по всем нечетным числам от 1 до 99 можно при помощи функции range(1, 100, 2), а сделать цикл по всем числам от 100 до 1 можно при помощи range(100, 0, 1). Более формально, цикл for i in range(a, b, d) при d > 0 задает значения индексной переменной i = a, i = a + d, i = a + 2 * d и так для всех значений, для которых i < b. Если же d > 0, то переменная цикла принимает все значения i > b. 18 8.2 Разбор задач 8.2.1 Задача «Ряд - 2» Даны два целых числа A и В. Выведите все числа от A до B включительно, в порядке возрастания, если A < B, или в порядке убывания в противном случае. Входные данные Вводятся два целых числа. Выходные данные Выведите ответ на задачу. Примеры входные данные 1 10 выходные данные 1 2 3 4 5 6 7 8 9 10 Разбор задачи «Ряд - 2» В этой задаче необходимо определить, какое из значений a и b больше, и в зависимости от этого увеличивать или уменьшать индексную переменную. Нужно было учесть, что значение b тоже должно быть выведено, поэтому в функции range() требовалось в качестве границы указать b+1 в случае возрастания индексной переменной и b-1 в случае убывания. В случае, когда значения a и b равны, выполнится ветка else и выведется единственное значение, поэтому отдельно этот случай можно не рассматривать: a=int(input()) b=int(input()) if a<b: for i in range(a,b+1): print(i) else: for i in range(a,b-1,-1): print(i) 8.2.2 Задача «Ряд - 3» Дано натуральное число n. Напечатайте все n-значные нечетные натуральные числа в порядке убывания. Входные данные Вводится натуральное число. Выходные данные Выведите ответ на задачу. Примеры входные данные 1 выходные данные 97531 Разбор задачи «Ряд - 3» Для решения данной задачи достаточно определить наибольшее и наименьшее n-значные числа и уменьшать индексную переменную с величиной изменения -2. Наибольшее n-значное число имеет вид 10n–1 (оно является нечетным, поэтому начинать диапазон нужно с него). Наименьшим является число 10n–1. Основная ошибка, которая встречалась в решениях, это уверенность, что наименьшее число является всегда четным, что неверно при n = 1, из-за чего при n = 1 программа отсекала число 1. По этой причине в качестве значения, до которого будет изменяться индексная переменная (не включая его!) нужно брать число 10n–1–1. Для того чтобы дважды не считать n-ю степень 10, можно сохранить это значение в переменную и использовать это значение в цикле: n = int(input()) k = 10**n for i in range(k-1, k//10-1, -2): 19 print(i) 8.2.3 Задача «Сумма кубов» По данному натуральном n вычислите сумму: 13+23+33+...+n3 Входные данные Вводится число n. Выходные данные Выведите ответ на задачу. Примеры входные данные 1 выходные данные 1 входные данные 3 выходные данные 36 Разбор задачи «Сумма кубов» В задаче нужно было завести переменную-накопитель, к которой на каждой итерации цикла добавляется куб следующего числа. Перед тем, как начать считать сумму кубов, переменнуюнакопитель необходимо проинициализировать нулем: n = int(input()) s = 0 for i in range(1, n+1): s += i**3 print(s) 8.2.4 Задача «Факториал» По данному целому неотрицательному n вычислите значение n!. Входные данные Вводится число n. Выходные данные Выведите ответ на задачу. Примеры входные данные 3 выходные данные 6 Разбор задачи «Факториал» Напомним, что факториалом целого натурального числа n называют произведение всех натуральных чисел от 1 до n. Если n = 0, то факториал по определению равен единице. В задаче нужно было завести переменную-накопитель, которая на каждой итерации цикла умножается на следующее число. В начале переменную-накопитель необходимо проинициализировать единицей. В случае, если n = 0, тело цикла не выполнится ниразу, в результате значение накопителя останется равным 1: n = int(input()) f = 1 for i in range(1, n+1): f *= i print(f) 8.2.5 Задача «Фибоначчиева последовательность» Последовательность чисел a1, a2, …, ai,… называется Фибоначчиевой, если для всех i≥3 верно, что ai=ai–1+ai–2, то есть каждый член последовательности (начиная с третьего) равен сумме двух предыдущих. 20 Ясно, что задавая различные числа a1 и a2 мы можем получать различные такие последовательности, и любая Фибоначчиева последовательность однозначно задается двумя своими первыми членами. Будем решать обратную задачу. Вам будет дано число N и два члена последовательности: aN и aN+1. Вам нужно написать программу, которая по их значениям найдет a1 и a2. Входные данные Вводятся число N и значения двух членов последователности: aN и aN+1 (1≤N≤30, члены последовательности — целые числа, по модулю не превышающие 100) Если вы пишите на языке программирования python, то считывание aN и aN+1 элементов должно быть организовано так: x, y = map(int, input().split()) Выходные данные Выведите два числа — значения первого и второго членов этой последовательности. Примеры входные данные 4 35 выходные данные 11 Рекомендации к задаче «Фибоначчиева последовательность» В этой задаче недопустимы решения с массивами (списками и другими структурами данных, для тех, кто их знает). Разбор Задачи «Фибоначчиева последовательность» В этой задаче необходимо проделать N–1 шаг для нахождения первого и второго элемента. Для этого воспользуемся циклом for с шагом –1. На каждом шаге будет вычислено предыдущее значение переменной. N=int(input()) x, y = map(int, input().split()) for i in range(N-1, 0, -1): (x, y) = (y - x, x) print(x, y) 8.2.6 Задача «Флаги» Напишите программу, которая по данному числу n от 1 до 9 выводит на экран n флагов. Изображение одного флага имеет размер 4×4 символов, между двумя соседними флагами также имеется пустой (из пробелов) столбец. Разрешается вывести пустой столбец после последнего флага. Внутри каждого флага должен быть записан его номер — число от 1 до n. Входные данные Вводится натуральное число. Выходные данные Выведите ответ на задачу. Примеры входные данные 3 выходные данные +___ +___ +___ |1 / |2 / |3 / |__\ |__\ |__\ ||| входные данные 1 выходные данные +___ |1 / |__\ | 21 Рекомендации к задаче «Флаги» Используйте операцию умножения строки на число для печати первой, третьей и четвертой строк. Для печати второй строки используйте цикл for. Для того чтобы печать в цикле производилась в строку, а не в столбик, измените спецификатор end оператора печати. А после цикла для перевода строки перед печатью третьей строки используйте пустой print(). Следите за количеством пробелов между флажками. Также нужно помнить, что для печати символа ‘\’ нужно использовать два таких символа подряд ‘\\’, так как сам по себе он является служебным. Разбор задачи «Флаги» Для решения задачи используется операция умножения строки на число (см. занятие 1) для печати первой, третьей и четвертой строк. Для печати второй строки используется цикл for. Для того чтобы печать в цикле производилась в строку, а не в столбик, спецификатор end оператора печати принимает значение пробела ‘ ‘. После цикла для перевода строки перед печатью третьей строки используется пустой print(). Также нужно помнить, что для печати символа ‘\’ нужно использовать два таких символа подряд ‘\\’, так как сам по себе он является служебным: n = int(input()) print('+___ '*n) for i in range(n): print('|', i+1, ' /', sep='', end=' ') print() print('|__\\ '*n) print('| '*n) 8.2.7 Задача «Чётные числа» По данным двум натуральным числам A и B (A≤B) выведите все чётные числа на отрезке от A до B. В этой задаче нельзя использовать инструкцию if. Входные данные Вводятся два натуральных числа A и B. Выходные данные Выведите ответ на задачу. Примеры входные данные 1 10 выходные данные 2 4 6 8 10 Рекомендации к задаче «Чётные числа» В этой задаче не допустимы решения с использованием инструкции if. Разбор задачи «Чётные числа» В этой задаче решения с использованием инструкции if были проигнорированы. Задача похожа на задачу «ряд 3» с той лишь разницей, что нужно определить четность начального значения индексной переменной. В случае, если a – нечетно, нужно выбрать в качестве начального значения a + 1, иначе начинать надо со значения а. Заметим, что если к числу а прибавить остаток от деления этого числа на 2, то получим первое четное число, которое необходимо вывести. Для конечного значения достаточно рассмотреть число b + 1. a = int(input()) b = int(input()) for i in range(a + a%2, b + 1, 2): print(i) 8.2.8 Задача «Количество нулей» Дано несколько чисел. Подсчитайте, сколько из них равны нулю, и выведите это количество. Входные данные Cначала вводится число N, затем вводится ровно N целых чисел. Выходные данные Выведите ответ на задачу. 22 Примеры входные данные 5 0 7 0 2 2 выходные данные 2 Рекомендации к задаче «Количество нулей» В этой задаче решения с массивами и аналогичными структурами данных не допустимы. Разбор задачи «Количество нулей» В этой задаче решения с массивами и аналогичными структурами данных были проигнорированы. Для решения задачи нужно было завести счетчик нулей и проинициализировать его нулем (изначально предполагаем, что нулей в последовательности нет). Далее, последовательно считываем значения и в случае, если считали ноль, увеличиваем счетчик на единицу: n = int(input()) k = 0 for i in range(n): a = int(input()) if a == 0: k += 1 print(k) 8.2.9 Задача «Диофантово уравнение - 2» Даны числа a, b, c, d, e. Подсчитайте количество таких целых чисел от 0 до 1000, которые являются корнями уравнения (ax3+bx2 +cx+d)/(x-e)=0, и выведите их количество. Входные данные Вводятся целые числа a, b, c, d и e. Выходные данные Выведите ответ на задачу. Примеры входные данные 1 -2 1 0 1 выходные данные 1 входные данные 1 1 1 1 1 выходные данные 0 Разбор задачи «Диофантово уравнение - 2» В этой задаче используется “информатический” подход к нахождению корней, а именно перебор всех возможных чисел из интересующего нас диапазона, и проверка, является ли очередное число корнем уравнения. Поскольку значение 1000 тоже должно проверяться, в качестве параметра функции range() надо брать число 1001. Также стоит обратить внимание на то, что делить на 0 нельзя, следовательно, надо проверить знаменатель на 0: 23 a=int(input()) b=int(input()) c=int(input()) d=int(input()) e=int(input()) count = 0 for i in range(0, 1001): if i - e != 0: if a*i**3 + b*i**2 + c*i + d == 0: count += 1 print(count) 8.2.10 Задача «Потерянная карточка» Для настольной игры используются карточки с номерами от 1 до N. Одна карточка потерялась. Найдите ее, зная номера оставшихся карточек. Входные данные Дано число N, далее N-1 номер оставшихся карточек (различные числа от 1 до N). Выходные данные Программа должна вывести номер потерянной карточки. Примечание Для самых умных – массивами и аналогичными структурами данных пользоваться нельзя. Примеры входные данные 5 1 2 3 4 выходные данные 5 входные данные 4 3 2 4 выходные данные 1 Разбор задачи «Потерянная карточка» В этой задаче решения с массивами и аналогичными структурами данных были проигнорированы. Для поиска пропущенной карточки требуется найти сумму всех карточек с номерами от 1 до N, и вычесть сумму номеров карточек, которые не потерялись. Вычислить сумму можно было честным образом, используя цикл for, или используя формулу суммы элементов арифметической прогрессии с разностью 1: Sn = n*(n+1)/2 Заметим, что Sn всегда является целым числом, поскольку одно из значений n или n+1 будет четным, поэтому можно пользоваться целочисленным делением: n = int(input()) result = n * (n + 1) // 2 for i in range(n - 1): x = int(input()) result -= x print(result) 24 9 Цикл while Цикл while (“пока”) позволяет выполнить одну и ту же последовательность действий, пока проверяемое условие истинно. Условие записывается до тела цикла и проверяется до выполнения тела цикла. Как правило, цикл while используется, когда невозможно определить точное значение количества проходов исполнения цикла. Синтаксис цикла while в простейшем случае выглядит так: while условие: блок инструкций При выполнении цикла while сначала проверяется условие. Если оно ложно, то выполнение цикла прекращается и управление передается на следующую инструкцию после тела цикла while. Если условие истинно, то выполняется инструкция, после чего условие проверяется снова и снова выполняется инструкция. Так продолжается до тех пор, пока условие будет истинно. Как только условие станет ложно, работа цикла завершится и управление передастся следующей инструкции после цикла. Например, следующий фрагмент программы напечатает на экран квадраты всех целых чисел от 1 до 10. Видно, что цикл while может заменять цикл for ... in range(...): i=1 while i <= 10: print(i) i += 1 В этом примере переменная i внутри цикла изменяется от 1 до 10. Такая переменная, значение которой меняется с каждым новым проходом цикла, называется счетчиком. Заметим, что после выполнения этого фрагмента значение переменной i будет равно 11, поскольку именно при i==11 условие i<=10 впервые перестанет выполняться. Вот еще один пример использования цикла while для определения количества цифр натурального числа n: n = int(input()) length = 0 while n > 0: n //= 10 length += 1 В этом цикле мы отбрасываем по одной цифре числа, начиная с конца, что эквивалентно целочисленному делению на 10 (n //= 10), при этом считаем в переменной length, сколько раз это было сделано. В языке Питон есть и другой способ решения этой задачи: length = len(str(i)). Инструкции управления циклом После тела цикла можно написать слово else: и после него блок операций, который будет выполнен один раз после окончания цикла, когда проверяемое условие станет неверно: i=1 while i <= 10: print(i) i += 1 else: print('Цикл окончен, i =', i) Казалось бы, никакого смысла в этом нет, ведь эту же инструкцию можно просто написать после окончания цикла. Смысл появляется только вместе с инструкцией break, использование которой внутри цикла приводит к немедленному прекращению цикла, и при этом не исполняется ветка else. Разумеется, инструкцию break осмыленно вызывать только из инструкции if, то есть она должна выполняться только при выполнении какого-то особенного условия. Другая инструкция управления циклом — continue (продолжение цикла). Если эта инструкция встречается где-то посередине цикла, то пропускаются все оставшиеся инструкции до конца цикла, и исполнение цикла продолжается со следующей итерации. 25 Инструкции break, continue и ветка else: можно использовать и внутри цикла for. Тем не менее, увлечение инструкциями break и continue не поощряется, если можно обойтись без их использования. Вот типичный пример плохого использования инструкции break. while True: length += 1 n //= 10 if n == 0: break 10 Строки Строка считывается со стандартного ввода функцией input(). Напомним, что для двух строк определена операция сложения (конкатенации), также определена операция умножения строки на число. Строка состоит из последовательности символов. Узнать количество символов (длину строки) можно при помощи функции len: >>> S = 'Hello' >>> print(len(S)) 5 10.1 Срезы (splices) Срез (splice) — извлечение из данной строки одного символа или некоторого фрагмента подстроки или подпоследовательности. Есть три формы срезов. Самая простая форма среза: взятие одного символа строки, а именно, S[i] — это срез, состоящий из одного символа, который имеет номер i, при этом считая, что нумерация начинается с числа 0. То есть если S='Hello', то S[0]=='H', S[1]=='e', S[2]=='l', S[3]=='l', S[4]=='o'. Номера символов в строке (а также в других структурах данных: списках, кортежах) называются индексом. Если указать отрицательное значение индекса, то номер будет отсчитываться с конца, начиная с номера -1. То есть S[-1]=='o', S[-2]=='l', S[-3]=='l', S[-4]=='e', S[-5]=='H'. Или в виде таблицы: Строка S H e l l o Индекс S[0] S[1] S[2] S[3] S[4] Индекс S[-5] S[-4] S[-3] S[-2] S[-1] Если же номер символа в срезе строки S больше либо равен len(S), или меньше, чем -len(S), то при обращении к этому символу строки произойдет ошибка IndexError: string index out of range. Срез с двумя параметрами: S[a:b] возвращает подстроку из b-a символов, начиная с символа c индексом a, то есть до символа с индексом b, не включая его. Например, S[1:4]=='ell', то же самое получится если написать S[-4:-1]. Можно использовать как положительные, так и отрицательные индексы в одном срезе, например, S[1:-1] — это строка без первого и последнего символа (срез начинается с символа с индексом 1 и заканчиватеся индексом -1, не включая его). При использовании такой формы среза ошибки IndexError никогда не возникает. Например, срез S[1:5] вернет строку 'ello', таким же будет результат, если сделать второй индекс очень большим, например, S[1:100] (если в строке не более 100 символов). Если опустить второй параметр (но поставить двоеточие), то срез берется до конца строки. Например, чтобы удалить из строки первый символ (его индекс равен 0, то есть взять срез, начиная с символа с индексом 1), то можно взять срез S[1:], аналогично если опустиить первый параметр, то срез берется от начала строки. То есть удалить из строки последний символ можно при помощи среза S[:-1]. Срез S[:] совпадает с самой строкой S. Если задать срез с тремя параметрами S[a:b:d], то третий параметр задает шаг, как в случае с функцией range, то есть будут взяты символы с индексами a, a+d, a+2*d и т.д. При задании значения третьего параметра, равному 2, в срез попадет кажый второй символ, а если взять значение среза, равное -1, то символы будут идти в обратном порядке 26 10.2 Методы Метод - это функция, применяемая к объекту, в данном случае - к строке. Метод вызывается в виде Имя_объекта.Имя_метода(параметры). Например, S.find("e") — это применение к строке S метода find с одним параметром "e". 10.2.1 Методы find и rfind Метод find находит в данной строке (к которой применяется метод) данную подстроку (которая передается в качестве параметра). Функция возвращает индекс первого вхождения искомой подстроки. Если же подстрока не найдена, то метод возвращает значение -1. Например: >>> S = 'Hello' >>> print(S.find('e')) 1 >>> print(S.find('ll')) 2 >>> print(S.find('L')) -1 Аналогично, метод rfind возвращает индекс последнего вхождения данной строки (“поиск справа”). >>> S = 'Hello' >>> print(S.find('l')) 2 >>> print(S.rfind('l')) 3 Если вызвать метод find с тремя параметрами S.find(T, a, b), то поиск будет осуществляться в срезе S[a:b]. Если указать только два параметра S.find(T, a), то поиск будет осуществляться в срезе S[a:], то есть начиная с символа с индексом a и до конца строки. Метод S.find(T, a, b) возращает индекс в строке S, а не индекс относительно начала среза. 10.2.2 Методы replace Метод replace заменяет все вхождения одной строки на другую. Формат: S.replace(old, new) — заменить в строке S все вхождения подстроки old на подстроку new. Пример: >>> 'Hello'.replace('l', 'L') 'HeLLo' Если методу replace задать еще один параметр: S.replace(old, new, count), то заменены будут не все вхождения, а только не больше, чем первые count из них. >>> 'Abrakadabra'.replace('a', 'A', 2A 'AbrAkAdabra' 10.2.3 Методы count Подсчитывает количество вхождений одной строки в другую строку. Простейшая форма вызова S.count(T) возвращает число вхождений строки T внутри строки S. При этом подсчитываются только непересекающиеся вхождения, например: >>> 'Abracadabra'.count('a') 4 >>> ('a' * 100000).count('aa') 50000 При указании трех параметров S.count(T, a, b), будет выполнен подсчет числа вхождений строки T в срез S[a:b]. 10.3 Разбор задач 10.3.1 Задача «Делаем срезы» Входные данные Дана строка. Выходные данные Сначала выведите третий символ этой строки. Во второй строке выведите предпоследний символ этой строки. 27 В третьей строке выведите первые пять символов этой строки. В четвертой строке выведите всю строку, кроме последних двух символов. В пятой строке выведите все символы с четными индексами (считая, что индексация начинается с 0, поэтому символы выводятся начиная с первого). В шестой строке выведите все символы с нечетными индексами, то есть начиная со второго символа строки. В седьмой строке выведите все символы в обратном порядке. В восьмой строке выведите все символы строки через один в обратном порядке, начиная с последнего. В девятой строке выведите длину данной строки. Примеры входные данные Abrakadabra выходные данные r r Abrak Abrakadab Arkdba baaar arbadakarbA abdkrA 11 Разбор задачи «Делаем срезы » В задаче нужно было внимательно следить за граничными значениями при работе со срезами. s = input() print(s[2]) print(s[-2]) print(s[:5]) print(s[:-2]) print(s[::2]) print(s[1::2]) print(s[::-1]) print(s[::-2]) print(len(s)) 10.3.2 Задача «Две половинки» Дана строка. Разрежьте ее на две равные части (если длина строки — четная, а если длина строки нечетная, то длина первой части должна быть на один символ больше). Переставьте эти две части местами, результат запишите в новую строку и выведите на экран. При решении этой задачи нельзя пользоваться инструкцией if. Входные данные Вводится строка. Выходные данные Выведите ответ на задачу. Примеры входные данные Hi выходные данные iH входные данные Hello выходные данные loHel Разбор задачи «Две половинки» 28 В данной задаче требовалось поменять местами первую и вторую половинки слова. Для этого нужно найти середину и поменять части местами. s = input() n = len(s) // 2 + len(s) % 2 print(s[n:] + s[:n]) 10.3.3 Задача «Переставить два слова» Дана строка, состоящая ровно из двух слов, разделенных пробелом. Переставьте эти слова местами. Результат запишите в строку и выведите получившуюся строку. При решении этой задачи нельзя пользоваться циклами и инструкцией if. Входные данные Вводится строка. Выходные данные Выведите ответ на задачу. Примеры входные данные Hello, world! выходные данные world! Hello, Рекомендации к задаче «Переставить два слова» При решении этой задачи на Python нельзя пользоваться циклами и инструкцией if. Решение задачи «Переставить два слова» Для решения нужно было найти в строке пробел и склеить 2 среза: до пробела и после пробела. Основная проблема при решении данной задачи – случайно оставленный пробел в конце строки. s = input() i = s.find(' ') str = s[i + 1:] + ' ' + s[:i] print(str) 10.3.4 Задача «Второе вхождение» Дана строка. Найдите в этой строке второе вхождение буквы f, и выведите индекс этого вхождения. Если буква f в данной строке встречается только один раз, выведите число -1, а если не встречается ни разу, выведите число -2. При решении этой задачи нельзя использовать метод count. Входные данные Вводится строка. Выходные данные Выведите ответ на задачу. Примеры входные данные comfort выходные данные -1 входные данные coffee выходные данные 3 Рекомендации к задаче «Второе вхождение» При решении этой задачи нельзя использовать метод count. Разбор задачи «Второе вхождение» При решении этой задачи нельзя было использовать метод count. Ниже представлено решение задачи. Если f встретилось хотя бы один раз, то нужно искать следующее вхождение, начиная со следующего символа. В случае, если больше вхождений не будет, распечатается –1 как результат работы метода find(). s = input() if s.find('f') == -1: print(-2) else: print(s.find('f', s.find('f') + 1)) 29 10.3.5 Задача «Количество слов» Дана строка, состоящая из слов, разделенных пробелами. Определите, сколько в ней слов. Используйте для решения задачи метод count. Входные данные Вводится строка. Выходные данные Выведите ответ на задачу. Примеры входные данные Hello world выходные данные 2 Разбор задачи «Количество слов» Поскольку между словами стоял ровно один пробел, достаточно было посчитать количество пробелов в строке, при этом количество слов больше на единицу. s =input() print(s.count(' ') + 1) 10.3.6 Задача «Обращение фрагмента» Дана строка, в которой буква h встречается как минимум два раза. Разверните последовательность символов, заключенную между первым и последнием появлением буквы h, в противоположном порядке. Входные данные Вводится строка. Выходные данные Выведите ответ на задачу. Примеры входные данные In the hole in the ground there lived a hobbit выходные данные In th a devil ereht dnuorg eht ni eloh ehobbit Разбор задачи «Обращение фрагмента» Необходимо найти первое и последнее вхождение символа h, после чего вывести 3 среза, описанных ниже: s = input() a = s.find('h') b = s.rfind('h') str = s[:a + 1] + s[b - 1:a:-1] + s[b:] print(str) 10.3.7 Задача «Замена внутри фрагмента» Дана строка. Замение в этой строке все появления буквы h на букву H, кроме первого и последнего вхождения. Входные данные Вводится строка. Выходные данные Выведите ответ на задачу. Примеры входные данные 30 In the hole in the ground there lived a hobbit выходные данные In the Hole in tHe ground tHere lived a hobbit Разбор задачи «Замена внутри фрагмента» Находим индекс первого и последнего вхождения символа h и для среза между найденными индексами производим замену с помощью метода replace(): a = input() b = a.find('h') c = a.rfind('h') print(a[:b+1],a[b+1:c:].replace('h','H'),a[c:],sep='') 10.3.8 Задача «Метод бутерброда» Секретное агентство «Super-Secret-no» решило для шифрования переписки своих сотрудников использовать «метод бутерброда». Сначала буквы слова нумеруются в таком порядке: первая буква получает номер 1, последняя буква - номер 2, вторая – номер 3, предпоследняя – номер 4, потом третья … и так для всех букв (см. рисунок). Затем все буквы записываются в шифр в порядке своих номеров. В конец зашифрованного слова добавляется знак «диез» (#), который нельзя использовать в сообщениях. Например, слово «sandwich» зашифруется в «shacnidw#». К сожалению, программист «Super-Secret-no», написал только программу шифрования и уволился. И теперь агенты не могут понять, что же они написали друг другу. Помогите им. Входные данные Вводится слово, зашифрованное методом бутерброда. Длина слова не превышает 20 букв. Выходные данные Выведите расшифрованное слово. Примеры входные данные Aabrrbaacda# выходные данные Abracadabra Разбор задачи «Метод бутерброда» В данной задаче не надо учитывать последний символ, поэтому правая граница будет до -1. Первая часть состоит из каждой второй буквы, начиная с нулевой s[:-1:2]. Вторая часть состоит из каждой второй буквы, начиная с первой, но при этом перевёрнутой. Вначале найдём вторую часть: s[1:-1:2], потом её перевернём: s[1:-1:2][::-1]. s = input() print(s[:-1:2] + s[1:-1:2][::-1]) 10.3.9 Задача «Благозвучное слово» Все буквы латинского алфавита делятся на гласные и согласные. Гласными буквами являются: a, e, i, o, u, y. Остальные буквы являются согласными. 31 Слово называется благозвучным, если в этом слове не встречается больше двух согласных букв подряд и не встречается больше двух гласных букв подряд. Например, слова abba, mama, program — благозвучные, а слова aaa, school, search — неблагозвучные. Вводится слово. Если это слово является неблагозвучным, то разрешается добавлять в любые места этого слова любые буквы. Определите, какое минимальное количество букв можно добавить в это слово, чтобы оно стало благозвучным. Входные данные Вводится слово, состоящее только из маленьких латинских букв. Длина слова не превышает 30 символов. Выходные данные Выведите минимальное число букв, которые нужно добавить в это слово, чтобы оно стало благозвучным. Комментарии к примерам тестов 1. Слово уже является благозвучным. 2. Достаточно добавить одну гласную букву, например, между буквами s и с Примеры входные данные program выходные данные 0 входные данные school выходные данные 1 Разбор задачи «Благозвучное слово» В данной задаче надо найти количество букв, необходимые для того, чтобы слово можно было сделать благозвучным. Для этого достаточно находя очередную неблагозвучную тройку вставлять один символ между вторым и третьим символом. Заведём переменную, которая отвечает за то, сколько подряд идущих символов было неблагозвучными. Положительное число говорит, что последние last символов были гласные, отрицательные – согласные. Если last становится равной 3 или -3, значит надо добавить одну букву и присвоить last соответственно 1 или -1. Выражение now * last > 0 проверяет является ли текущая буква такой же гласности, как предыдущая. s = input() vowels = "aeiouy" count = 0 last = 0 for i in range(len(s)): now = vowels.find(s[i]) if now != -1: now = 1 if now * last > 0: last += now if last == 3 or last == -3: count += 1 last = now else: last = now print(count) 10.3.10Задача «Телефонные номера» Телефонные номера в адресной книге мобильного телефона имеют один из следующих форматов: +7<код><номер> 8<код><номер> <номер> 32 где <номер> — это семь цифр, а <код> — это три цифры или три цифры в круглых скобках. Если код не указан, то считается, что он равен 495. Кроме того, в записи телефонного номера может стоять знак “-” между любыми двумя цифрами (см. пример). На данный момент в адресной книге телефона Васи записано всего три телефонных номера, и он хочет записать туда еще один. Но он не может понять, не записан ли уже такой номер в телефонной книге. Помогите ему! Два телефонных номера совпадают, если у них равны коды и равны номера. Например, +7(916)0123456 и 89160123456 — это один и тот же номер. Входные данные В первой строке входных данных записан номер телефона, который Вася хочет добавить в адресную книгу своего телефона. В следующих трех строках записаны три номера телефонов, которые уже находятся в адресной книге телефона Васи. Гарантируется, что каждая из записей соответствует одному из трех приведенных в условии форматов. Выходные данные Для каждого телефонного номера в адресной книге выведите YES (заглавными буквами), если он совпадает с тем телефонным номером, который Вася хочет добавить в адресную книгу или NO (заглавными буквами) в противном случае. Пример Ввод Вывод 8(495)430-23-97 YES +7-4-9-5-43-023-97 YES 4-3-0-2-3-9-7 NO 8-495-430 Разбор задачи «Телефонные номера» В данной задаче надо аккуратно преобразовать исходный номер телефона к единому стандарту, а потом сравнить эти телефоны. tel = '' for i in range(4): s = input() s = s.replace('+7', '8') s = s.replace('(', '') s = s.replace(')', '') s = s.replace('-', '') if len(s) == 7: s = '8495' + s tel += s if tel[:11] == tel[11:22]: print('YES') else: print('NO') if tel[:11] == tel[22:33]: print('YES') else: print('NO') if tel[:11] == tel[33:]: print('YES') else: print('NO') 11 Массивы (списки) 33 11.1 Основные сведения Большинство программ работает не с отдельными переменными, а с набором переменных. Например, программа может обрабатывать информацию об учащихся класса, считывая список учащихся с клавиатуры или из файла, при этом изменение количества учащихся в классе не должно требовать модификации исходного кода программы. Раньше мы сталкивались с задачей обработки элементов последовательности, например, вычисляя наибольший элемент последовательности. Но при этом мы не сохраняли всю последовательность в памяти компьютера, однако, во многих задачах нужно именно сохранять всю последовательность, например, если бы нам требовалось вывести все элементы последовательности в возрастающем порядке (“отсортировать последовательность”). Для хранения таких данных можно использовать структуру данных, называемую в Питоне список (в большинстве же языков программирования используется другой термин “массив”). Список представляет собой последовательность элементов, пронумерованных от 0, как символы в строке. 11.2 Ввод (формирование) элементов списка Primes = [2, 3, 5, 7, 11, 13] Rainbow = ['Red', 'Orange', 'Yellow', 'Green', 'Blue', 'Indigo', 'Violet'] В списке Primes — 6 элементов, а именно, Primes[0] == 2, Primes[1] == 3, Primes[2] == 5, Primes[3] == 7, Primes[4] == 11, Primes[5] == 13. Список Rainbow состоит из 7 элементов, каждый из которых является строкой. Также как и символы строки, элементы списка можно индексировать отрицательными числами с конца, например, Primes[-1] == 13, Primes[-6] == 2. Длину списка, то есть количество элементов в нем, можно узнать при помощи функции len, например, len(A) == 6. Рассмотрим несколько способов создания и считывания списков. Прежде всего можно создать пустой список (не содержащий элементов, длины 0), в конец списка можно добавлять элементы при помощи метода append. Например, если программа получает на вход количество элементов в списке n, а потом n элементов списка по одному в отдельной строке, то организовать считывание списка можно так: A = [] for i in range(int(input()): A.append(int(input()) В этом примере создается пустой список, далее считывается количество элементов в списке, затем по одному считываются элементы списка и добавляются в его конец. Для списков целиком определены следующие операции: конкатенация списков (добавление одного списка в конец другого) и повторение списков (умножение списка на число). Например: A = [1, 2, 3] B = [4, 5] C=A+B D=B*3 В результате список C будет равен [1, 2, 3, 4, 5], а список D будет равен [4, 5, 4, 5, 4, 5]. Это позволяет по-другому организовать процесс считывания списков: сначала считать размер списка и создать список из нужного числа элементов, затем организовать цикл по переменной i начиная с числа 0 и внутри цикла считывается i-й элемент списка: A = [0] * int(input()) for i in range(len(A)): A[i] = int(input()) Вывести элементы списка A можно одной инструкцией print(A), при этом будут выведены квадратные скобки вокруг элементов списка и запятые между элементами списка. Такой вывод неудобен, чаще требуется просто вывести все элементы списка в одну строку или по одному элементу в строке. Приведем два примера, также отличающиеся организацией цикла: for i in range(len(A)): print(A[i]) Здесь в цикле меняется индекс элемента i, затем выводится элемент списка с индексом i. 34 for elem in A: print(elem, end = ' ') В этом примере элементы списка выводятся в одну строку, разделенные пробелом, при этом в цикле меняется не индекс элемента списка, а само значение переменной (например, в цикле for elem in ['red', 'green', 'blue'] переменная elem будет последовательно принимать значения 'red', 'green', 'blue'. 11.3 Ввод-вывод: методы split и join Элементы списка могут вводиться по одному в строке, в этом случае строку можно считать функцией input(). После этого можно использовать метод строки split, возвращающий список строк, разрезав исходную строку на части по пробелам. Пример: A = input().split() Если при запуске этой программы ввести строку 1 2 3, то список A будет равен ['1', '2', '3']. Обратите внимание, что список будет состоять из строк, а не из чисел. Если хочется получить список именно из чисел, то можно затем элементы списка по одному преобразовать в числа: for i in range(len(A)): A[i] = int(A[i]) Используя функции языка map и list то же самое можно сделать в одну строку: A = list(map(int, input().split())) Объяснений, как работает этот код, пока не будет. Если нужно считать список действительных чисел, то нужно заменить тип int на тип float. У метода split есть необязательный параметр, который определяет, какая строка будет использоваться в качестве разделителя между элементами списка. Например, метод split('.') вернет список, полученный разрезанием исходной строки по символам '.'. Используя “обратные” методы можно вывести список при помощи однострочной команды. Для этого используется метод строки join. У этого метода один параметр: список строк. В результате получается строка, полученная соединением элементов списка (которые переданы в качестве параметра) в одну строку, при этом между элементами списка вставляется разделитель, равный той строке, к которой применяется метод. Например программа A = ['red', 'green', 'blue'] print(' '.join(A)) print(''.join(A)) print('***'.join(A)) выведет строки 'red green blue', redgreenblue и red***green***blue. Если же список состоит из чисел, то придется использовать еще и функцию map. То есть вывести элементы списка чисел, разделяя их пробелами, можно так: print(' '.join(map(str, A))) 11.4 Срезы (splices) Со списками, так же как и со строками, можно делать срезы. А именно: A[i:j] срез из j-i элементов A[i], A[i+1], ..., A[j-1]. A[i:j:-1] срез из i-j элементов A[i], A[i-1], ..., A[j+1] (то есть меняется порядок элементов). A[i:j:k] срез с шагом k: A[i], A[i+k], A[i+2*k],... . Если значение k<0, то элементы идут в противоположном порядке. Каждое из чисел i или j может отсутствовать, что означает “начало строки” или “конец строки” Списки, в отличии от строк, являются изменяемыми объектами: можно отдельному элементу списка присвоить новое значение. Но можно менять и целиком срезы. Например: A = [1, 2, 3, 4, 5] A[2:4] = [7, 8, 9] Получится список, у которого вместо двух элементов среза A[2:4] вставлен новый список уже из трех элементов. Теперь список стал равен [1, 2, 3, 7, 8, 9, 5]. A = [1, 2, 3, 4, 5, 6, 7] A[::-2] = [10, 20, 30, 40] Получится список [40, 2, 30, 4, 20, 6, 10]. Здесь A[::-2] — это список из элементов A[-1], A[-3], A[-5], A-7, которым присваиваются значения 10, 20, 30, 40 соответственно. 35 Если не непрерывному срезу (то есть срезу с шагом k, отличному от 1), присвоить новое значение, то количество элементов в старом и новом срезе обязательно должно совпадать, в противном случае произойдет ошибка ValueError. Обратите внимание, A[i] — это элемент списка, а не срез! 11.5 Операции со списками Со списками можно легко делать много разных операций. x in A Проверить, содержится ли элемент в списке. Возвращает True или False x not in A То же самое, что not(x in A) min(A) Наименьший элемент списка max(A) Наибольший элемент списка Индекс первого вхождения элемента x в список, при его отсутствии генерирует A.index(x) исключение ValueError A.count(x) Количество вхождений элемента x в список 11.6 Обработка и вывод вложенных списков Часто в задачах приходится хранить прямоугольные таблицы с данными. Такие таблицы называются матрицами или двумерными массивами. В языке программирования Питон таблицу можно представить в виде списка строк, каждый элемент которого является в свою очередь списком, например, чисел. Например, создать числовую таблицу из двух строк и трех столбцов можно так: A = [ [1, 2, 3], [4, 5, 6] ] Здесь первая строка списка A[0] является списком из чисел [1, 2, 3]. То есть A[0][0] == 1, значение A[0][1] == 2, A[0][2] == 3, A[1][0] == 4, A[1][1] == 5, A[1][2] == 6. Для обработки и вывода списка как правило используется два вложенных цикла. Первый цикл по номеру строки, второй цикл по элементам внутри строки. Например, вывести двумерный числовой список на экран построчно, разделяя числа пробелами внутри одной строки, можно так: for i in range(len(A)): for j in range(len(A[i]): print(A[i][j], end = ' ') print() То же самое, но циклы не по индексу, а по значениям списка: for row in A: for elem in row: print(elem, end = ' ') print() Естественно для вывода одной строки можно воспользоваться методом join: for row in A: print(' '.join(list(map(str, row)))) Используем два вложенных цикла для подсчета суммы всех чисел в списке: S=0 for i in range(len(A)): for j in range(len(A[i])): S += A[i][j] Или то же самое с циклом не по индексу, а по значениям строк: S=0 for row in A: for elem in row: S += elem Создание вложенных списков Пусть даны два числа: количество строк n и количество столбцов m. Необходимо создать список размером n×m, заполненный нулями. Очевидное решение оказывается неверным: A = [ [0] * m ] * n 36 В этом легко убедиться, если присвоить элементу A[0][0] значение 1, а потом вывести значение другого элемента A[1][0] — оно тоже будет равно 1! Дело в том, что [0] * m возвращает ccылку на список из m нулей. Но последующее повторение этого элемента создает список из n элементов, которые являются ссылкой на один и тот же список (точно так же, как выполнение операции B = A для списков не создает новый список), поэтому все строки результирующего списка на самом деле являются одной и той же строкой. Таким образом, двумерный список нельзя создавать при помощи операции повторения одной строки. Что же делать? Первый способ: сначала создадим список из n элементов (для начала просто из n нулей). Затем сделаем каждый элемент списка ссылкой на другой одномерный список из m элементов: A = [0] * n for i in range(n): A[i] = [0] * m Другой (но похожий) способ: создать пустой список, потом n раз добавить в него новый элемент, являющийся списком-строкой: A = [] for i in range(n): A.append([0] * m) Но еще проще воспользоваться генератором: создать список из n элементов, каждый из которых будет списком, состоящих из m нулей: A = [ [0] * m for i in range(n)] В этом случае каждый элемент создается независимо от остальных (заново конструируется список [0] * m для заполнения очередного элемента списка), а не копируются ссылки на один и тот же список. Ввод двумерного массива Пусть программа получает на вход двумерный массив, в виде n строк, каждая из которых содержит m чисел, разделенных пробелами. Как их считать? Например, так: A = [] for i in range(n): A.append(list(map(int, input().split()))) Или, без использования сложных вложенных вызовов функций: A = [] for i in range(n): row = input().split() for i in range(len(row)): row[i] = int(row[i]) A.append(row) Можно сделать то же самое и при помощи генератора: A = [ list(map(int, input().split())) for i in range(n)] Пример обработки двумерного массива Пусть дан квадратный массив из n строк и n столбцов. Необходимо элементам, находящимся на главной диагонали, проходящей из левого верхнего угла в правый нижний (то есть тем элементам A[i][j], для которых i==j) присвоить значение 1, элементам, находящимся выше главной диагонали – значение 0, элементам, находящимся ниже главной диагонали – значение 2. То есть получить такой массив (пример для n==4): 1000 2100 2210 2221 Рассмотрим несколько способов решения этой задачи. Элементы, которые лежат выше главной диагонали – это элементы A[i][j], для которых i<j, а для элементов ниже главной диагонали i>j. Таким образом, мы можем сравнивать значения i и j и по ним определять значение A[i][j]. Получаем следующий алгоритм: for i in range(n): 37 for j in range(n): if i < j: A[i][j] = 0 elif i > j: A[i][j] = 2 else: A[i][j] = 1 Данный алгоритм плох, поскольку выполняет одну или две инструкции if для обработки каждого элемента. Если мы усложним алгоритм, то мы сможем обойтись вообще без условных инструкций. Сначала заполним главную диагональ, для чего нам понадобится один цикл: for i in range(n): A[i][i] = 1 Затем заполним значением 0 все элементы выше главной диагонали, для чего нам понадобится в каждой из строк с номером i присвоить значение элементам A[i][j] для j=i+1, ..., n-1. Здесь нам понадобятся вложенные циклы: for i in range(n): for j in range(i + 1, n): A[i][j] = 0 Аналогично присваиваем значение 2 элементам A[i][j] для j=0, ..., i-1: for i in range(n): for j in range(0, i): A[i][j] = 2 Можно также внешние циклы объединить в один и получить еще одно, более компактное решение: for i in range(n): for j in range(0, i): A[i][j] = 2 A[i][i] = 1 for j in range(i + 1, n): A[i][j] = 0 А вот такое решение использует операцию повторения списков для построения очередной строки списка. i-я строка списка состоит из i чисел 2, затем идет одно число 1, затем идет n-i-1 число 0: for i in range(n): A[i] = [2] * i + [1] + [0] * (n - i - 1) А можно заменить цикл на генератор: A = [ [2] * i + [1] + [0] * (n - i - 1) for i in range(n)] Вложенные генераторы двумерных массивов Для создания двумерных массивов можно использовать вложенные генераторы, разместив генератор списка, являющегося строкой, внутри генератора для строк. Например, сделать список из n строк и m столбцов при помощи генератора, создающего список из n элементов, каждый элемент которого является списком из m нулей: [ [0] * m for i in range(n)] Но при этом внутренний список также можно создать при помощи, например, такого генератора: [0 for j in range(m)]. Вложив один генератор в другой получим вложенные генераторы: [ [0 for j in range(m)] for i in range(n)] Но если число 0 заменить на некоторое выражение, зависящее от i (номер строки) и j (номер столбца), то можно получить список, заполненный по некоторой формуле. В этом листке вам нужно будет придумать генераторы для заданных двумерных массивов. Например, пусть нужно задать следующий массив (для удобства добавлены дополнительные пробелы между элементами): 0 0 0 0 0 0 0 1 2 3 4 5 0 2 4 6 8 10 0 3 6 9 12 15 38 0 4 8 12 16 20 В этом массиве n = 5 строк, m = 6 столбцов, и элемент в строке i и столбце j вычисляется по формуле: A[i][j] = i * j. Для создания такого массива можно использовать генератор: [[ i * j for j in range(m)] for i in range(n)] 12 Функции Ранее была задача вычисления числа сочетаний из n элементов по k, для чего необходимо вычисление факториалов трех величин: n, k и n-k. Для этого можно сделать три цикла, что приводит к увеличению размера программы за счет трехкратного повторения похожего кода. Вместо этого лучше сделать одну функцию, вычисляющую факториал любого данного числа n и трижды использовать эту функцию в своей программе. Соответствующая функция может выглядеть так: def factorial(n): f=1 for i in range(2, n + 1): f *= i return f Этот текст должен идти в начале программы, вернее, до того места, где мы захотим воспользоваться функцией factorial. Первая строчка этого примера является описанием нашей функции. factorial — идентификатор, то есть имя нашей функции. После идентификатора в круглых скобках идет список параметров, которые получает наша функция. Список состоит из перечисленных через запятую идентификаторов параметров. В нашем случае список состоит из одной величины n. В конце строки ставится двоеточие. Далее идет тело функции, оформленное в виде блока, то есть с отступом. Внутри функции вычисляется значение факториала числа n и оно сохраняется в переменной f. Функция завершается инструкцией return f, которая завершает работу функции и возвращает значение переменной f. Инструкция return может встречаться в произвольном месте функции, ее исполнение завершает работу функции и возвращает указанное значение в место вызова. Если функция не возвращает значения, то инструкция return используется без возвращаемого значения, также в функциях, не возвращающих значения, инструкция return может отсутствовать. Теперь мы можем использовать нашу функцию несколько раз. В этом примере мы трижды вызываем функцию factorial для вычисления трех факториалов: factorial(n), factorial(k), factorial(n-k). n = int(input()) k = int(input()) print factorial(n) // (factorial(k) * factorial(n - k)) Мы также можем, например, объявить функцию binomial, которая принимает два целочисленных параметра n и k и вычисляет число сочетаний из n по k: def binomial(n, k) return factorial(n) // (factorial(k) * factorial(n - k)) Тогда в нашей основной программе мы можем вызвать функцию binomial для нахождения числа сочетаний: print binomial(n, k) Вернемся к задаче нахождения наибольшего из двух или трех чисел. Функцию нахождения максимума из двух чисел можно написать так: def max(a, b): if a > b: return a else: 39 return b Теперь мы можем реализовать функцию max3, находящую максимум трех чисел: def max3(a, b, c): return max(max(a, b), c) Функция max3 дважды вызывает функцию max для двух чисел: сначала, чтобы найти максимум из a и b, потом чтобы найти максимум из этой величины и c. Локальные и глобальные переменные Внутри функции можно использовать переменные, объявленные вне этой функции def f(): print a a=1 f() Здесь переменной a присваивается значение 1, и функция f печатает это значение, несмотря на то, что выше функции f эта переменная не инициализируется. Но в момент вызова функции f переменной a уже присвоено значение, поэтому функция f может вывести его на экран. Такие переменные (объявленные вне функции, но доступные внутри функции) называются глобальными. Но если инициализировать какую-то переменную внутри функции, использовать эту переменную вне функции не удастся. Например: def f(): a=1 f() print(a) Получим NameError: name 'a' is not defined. Такие переменные, объявленные внутри функции, называются локальными. Эти переменные становятся недоступными после выхода из функции. Интересным получится результат, если попробовать изменить значение глобальной переменной внутри функции: def f(): a=1 print(a) a=0 f() print(a) Будут выведены числа 1 и 0. То есть несмотря на то, что значение переменной a изменилось внутри функции, то вне функции оно осталось прежним! Это сделано в целях “защиты” глобальных переменных от случайного изменения из функции (например, если функция будет вызвана из цикла по переменной i, а в этой функции будет использована переменная i также для организации цикла, то эти переменные должны быть различными). То есть если внутри функции модифицируется значение некоторой переменной, то переменная с таким именем становится локальной переменной, и ее модификация не приведет к изменению глобальной переменной с таким же именем. Более формально: интерпретатор Питон считает переменную локальной, если внутри нее есть хотя бы одна инструкция, модифицирующая значение переменной (это может быть оператор =, += и т.д., или использование этой переменной в качестве параметра цикла for, то эта переменная считается локальной и не может быть использована до инициализации. При этом даже если инструкция, модицифицирующая переменную никогда не будет выполнена: интерпретатор это проверить не может, и переменная все равно считается локальной. Пример: def f(): print (a) 40 if False: a=0 a=1 f() Возникает ошибка: UnboundLocalError: local variable 'a' referenced before assignment. А именно, в функции f идентификатор a становится локальной переменной, т.к. в функции есть команда, модифицирующая переменную a, пусть даже никогда и не выполняющийся (но интерпретатор не может это отследить). Поэтому вывод переменной a приводит к обращению к неинициализированной локальной переменной. Чтобы функция могла изменить значение глобальной переменной, необходимо объявить эту переменную внутри функции, как глобальную, при помощи ключевого слова global: def f(): global a a=1 print (a) a=0 f() print(a) В этом примере на экран будет выведено 1 1, так как переменная a объявлена, как глобальная, и ее изменение внутри функции приводит к тому, что и вне функции переменная будет доступна. Тем не менее, лучше не изменять значения глобальных переменных внутри функции. Если функция должна поменять какую-то переменную, то как правило это лучше сделать, как значение, возвращаемое функцией. Если нужно, чтобы функция вернула не одно значение, а два или более, то для этого функция может вернуть кортеж из двух или нескольких значений: return (a, b) Тогда результат вызова функции тоже нужно присваивать кортежу: (n, m) = f(a, b) 12.1 Рекурсия Эпиграф: def ShortStory(): print("У попа была собака, он ее любил.") print("Она съела кусок мяса, он ее убил,") print("В землю закопал и надпись написал:") ShortStory() Как мы видели выше, функция может вызывать другую функцию. Но функция также может вызывать и саму себя! Рассмотрим это на примере функции вычисления факториала. Хорошо известно, что 0!=1, 1!=1. А как вычислить величину n! для большого n? Если бы мы могли вычислить величину (n−1)! , то тогда мы легко вычислим n!, поскольку n!=n(n−1)!. Но как вычислить (n−1)!? Если бы мы вычислили (n−2)!, то мы сможем вычисли и (n−1)!=(n−1)(n−2)!. А как вычислить (n−2)!? Если бы... В конце концов, мы дойдем до величины 0!, которая равна 1. Таким образом, для вычисления факториала мы можем использовать значение факториала для меньшего числа. Это можно сделать и в программе на C++: def factorial (n): if n == 0: return 1 else: return n * factorial(n - 1) 41 Подобный прием (вызов функцией самой себя) называется рекурсией, а сама функция называется рекурсивной. Рекурсивные функции являются мощным механизмом в программировании. К сожалению, они не всегда эффективны (об этом речь пойдет позже). Также часто использование рекурсии приводит к ошибкам, наиболее распространенная из таких ошибок – бесконечная рекурсия, когда цепочка вызовов функций никогда не завершается и продолжается, пока не кончится свободная память в компьютере. Пример бесконечной рекурсии приведен в эпиграфе к этому разделу. Две наиболее распространенные причины для бесконечной рекурсии: Неправильное оформление выхода из рекурсии. Например, если мы в программе вычисления факториала забудем поставить проверку if n == 0, то factorial(0) вызовет factorial(-1), тот вызовет factorial(-2) и т.д. Рекурсивный вызов с неправильными параметрами. Например, если функция factorial(n) будет вызывать factorial(n), то также получиться бесконечная цепочка. Поэтому при разработке рекурсивной функции необходимо прежде всего оформлять условия завершения рекурсии и думать, почему рекурсия когда-либо завершит работу. 13 Файловый ввод-вывод 13.1 Открытие файла Для каждого файла, с которым необходимо производить операции ввода-вывода, нужно связать специальный объект - поток. Открытие файла осуществляется функцией open, которой нужно передать два параметра. Первый параметр (можно также использовать именованный параметр file) имеет значение типа str, в котором записано имя открываемого файла. Второй параметр (можно также использовать именованный параметр mode) —это значение типа str, которое равно "r", если файл открывается для чтения данных (read), "w", если на запись (write), при этом содержимое файла очищается, и "a" — для добавления данных в конец файла (append). Если второй параметр не задан, то считается, что файл открывается в режиме чтения. Функция open возвращает ссылку на файловый объект, которую нужно записать в переменную, чтобы потом через данный объект использовать методы ввода-вывода. Например: input = open('input.txt', 'r') output = open('output.txt', 'w') Чтение данных из файла Для файла, открытого на чтение данных, можно вызывать следующие методы, позволяющие читать данные из файла. Метод readline() считывает одну строку из файла (до символа конца строки '\n', возвращается считанная строка вместе с символом '\n'. Если считывание не было успешно (достигнут конец файла), то возвращается пустая строка. Для удаления символа '\n' из конца файла удобно использовать метод строки rstrip(). Например: s = s.rstrip(). Метод readlines() считывает все строки из файла и возвращает список из всех считанных строк (одна строка — один элемент списка). При этом символы '\n' остаются в концах строк. Метод read() считывает все содержимое из файла и возвращает строку, которая может содержать символы '\n'. Если методу read передать целочисленный параметр, то будет считано не более заданного количества символов. Например, считывать файл побайтово можно при помощи метода read(1). Вывод данных в файл Данные выводятся в файл при помощи метода write, которому в качестве параметра передается одна строка. Этот метод не выводит символ конца строки '\n' (как это делает функция print при стандартном выводе), поэтому для перехода на новую строку в файле необходимо явно вывести символ '\n'. Также можно выводить данные в файл при помощи функции print, если передать ей еще один именованный параметр file, равный ссылке на открытый файл. Например: output = open('output.txt', 'w') print(a, b, c, file=output) Закрытие файла 42 После окончания работы с файлом необходимо закрыть его при помощи метода close(). Пример Следующая программа считывает все содержимое файла input.txt, записывает его в переменную s, а затем выводит ее в файл output.txt. input = open('input.txt', 'r') output = open('output.txt', 'w') s = input.read() output.write(s) input.close() output.close() А вот аналогичная программа, но читающая данные посимвольно: input = open('input.txt', 'r') output = open('output.txt', 'w') c = input.read(1) while len(c) > 0: output.write(c) c = input.read(1) input.close() output.close() 14 Множества Множество в языке Питон — это структура данных, эквивалентная множствам в математике. Множество может состоять из различных элементов, порядок элементов в множестве неопределен. В множество можно добавлять и удалять элементы, можно перебирать элементы множества, можно выполнять операции над множествами (объединение, пересечение, разность). Можно проверять принадлежность элементу множества. В отличии от массивов, где элементы хранятся в виде последовательного списка, в множествах порядок хранения элементов неопределен (более того, элементы множества храняться не подряд, как в списке, а при помощи хитрых алгоритмов). Это позволяет выполнять операции типа “проверить принадлежность элемента множеству” быстрее, чем просто перебирая все элементы множества. Элементами множества может быть любой неизменяемый тип данных: числа, строки, кортежи. Изменяемые типы данных не могут быть элементами множества, в частности, нельзя сделать элементом множества список (но можно сделать кортеж) или другое множество. Требование неизменяемости элементов множества накладывается особенностями представления множества в памяти компьютера. Задание множеств Множество задается перечислением всех его элементов в фигурных скобках. Например: A = {1, 2, 3} Исключением явлеется пустое множество, которое можно создать при помощи функции set(). Если функции set передать в качестве параметра список, строку или кортеж, то она вернет множество, составленное из элементов списка, строки, кортежа. Например: A = set('qwerty') print(A) выведет {'e', 'q', 'r', 't', 'w', 'y'}. Каждый элемент может входить в множество только один раз, порядок задания элементов не важен. Например, программа: A = {1, 2, 3} B = {3, 2, 3, 1} print(A == B) выведет True, так как A и B — равные множества. Каждый элемент может входить в множество только один раз. set('Hello') вернет множество из четырех элементов: {'H', 'e', 'l', 'o'}. Работа с элементами множеств Узнать число элементов в множестве можно при помощи функции len. 43 Перебрать все элементы множества (в неопределенном порядке!) можно при помощи цикла for: C = {1, 2, 3, 4, 5} for elem in C: print(elem) Проверить, принадлежит ли элемент множеству можно при помощи операции in, возвращающей значение типа bool: i in A Аналогично есть противоположная операция not in. Для добавления элемента в множество есть метод add: A.add(x) Для удаления элемента x из множества есть два метода: discard и remove. Их поведение различается только в случае, когда удаляемый элемент отсутствует в множестве. В этом случае метод discard не делает ничего, а метод remove генерирует исключение KeyError. Наконец, метод pop удаляет из множетсва один случайный элемент и возвращает его значение. Если же множество пусто, то генерируется исключение KeyError. Из множества можно сделать список при помощи функции list. Перебор элементов множества При помощи цикла for можно перебрать все элементы множества: Primes = {2, 3, 5, 7, 11} for num im Primes: print(num) Операции с множествами С множествами в питоне можно выполнять обычные для математики операции над множествами. A|B Возвращает множество, являющееся объединением множеств A и B. A.union(B) A |= B Добавляет в множество A все элементы из множества B. A.update(B) A&B Возвращает множество, являющееся пересечением множеств A и B. A.intersection(B) A &= B Оставляет в множестве A только те элементы, которые есть в A.intersection_update(B) множестве B. A-B Возвращает разность множеств A и B (элементы, входящие в A, но A.difference(B) не входящие в B). A -= B Удаляет из множества A все элементы, входящие в B. A.difference_update(B) A^B Возвращает симметрическую разность множеств A и B (элементы, A.symmetric_difference(B) входящие в A или в B, но не в оба из них одновременно). A ^= B Записывает в A симметрическую разность множеств A и B. A.symmetric_difference_update(B) A <= B Возвращает true, если A является подмножеством B. A.issubset(B) A >= B Возвращает true, если B является подмножеством A. A.issuperset(B) A<B Эквивалентно A <= B and A != B A>B Эквивалентно A >= B and A != B 15 Словари Обычные списки (массивы) представляют собой набор пронумерованных элементов, то есть для обращения к какому-либо элементу списка необходимо указать его номер. Номер элемента в списке однозначно идентифицирует сам элемент. Но идентифицировать данные по числовым номерам не всегда оказывается удобно. Например, маршруты поездов в России идентифицируются численнобуквенным кодом (число и одна цифра), также численно-буквенным кодом идентифицируются 44 авиарейсы, то есть для хранения информации о рейсах поездов или самолетов в качестве идентификатора удобно было бы использовать не число, а текстовую строку. Структура данных, позволяющая идентифицировать ее элементы не по числовому индексу, а по произвольному, называется словарем или ассоциативным массивом. Соответствующая структура данных в языке Питон называется dict. Рассмотрим простой пример использования словаря. Заведем словарь Capitals, где индексом является название страны, а значением — название столицы этой страны. Это позволит легко определять по строке с названием страны ее столицу. # Создадим пустой словать Capitals Capitals = dict() # Заполним его несколькими значениями Capitals['Russia'] = 'Moscow' Capitals['Ukraine'] = 'Kiev' Capitals['USA'] = 'Washington' # Считаем название страны print('В какой стране вы живете?') country = input() # Проверим, есть ли такая страна в словаре Capitals if country in Capitals: # Если есть - выведем ее столицу print('Столица вашей страны', Capitals[country]) else: # Запросим название столицы и добавив его в словарь print('Как называется столица вашей страны?') city = input() Capitals[country] = city Итак, каждый элемент словаря состоит из двух объектов: ключа и значения. В нашем примере ключом является название страны, значением является название столицы. Ключ идентифицирует элемент словаря, значение является данными, которые соответствуют данному ключу. Значения ключей — уникальны, двух одинаковых ключей в словаре быть не может. В жизни широко распространены словари, например, привычные бумажные словари (толковые, орфографические, лингвистические). В них ключом является слово-заголовок статьи, а значением — сама статья. Для того, чтобы получить доступ к статье, необходимо указать слово-ключ. Другой пример словаря, как структуры данных — телефонный справочник. В нем ключом является имя, а значением — номер телефона. И словарь, и телефонный справочник хранятся так, что легко найти элемент словаря по известному ключу (например, если записи хранятся в алфавитном порядке ключей, то легко можно найти известный ключ, например, бинарным поиском), но если ключ неизвествен, а известно лишь значение, то поиск элемента с данным значением может потребовать последовательного просмотра всех элементов словаря. Особенностью ассоциативного массива является его динамичность: в него можно добавлять новые элементы с произвольными ключами и удалять уже существующие элементы. При этом размер используемой памяти пропорционален размеру ассоциативного массива. Доступ к элементам ассоциативного массива выполняется хоть и медленнее, чем к обычным массивам, но в целом довольно быстро. В языке Питон как ключом может быть произвольный неизменяемый тип данных: целые и действительные числа, строки, кортежи. Ключом в словаре не может быть множество, но может быть элемент типа frozenset: специальный тип данных, являющийся аналогом типа set, который нельзя изменять после создания. Значением элемента словаря может быть любой тип данных, в том числе и изменяемый. Когда нужно использовать словари 45 Словари нужно использовать в следующих случаях: Подсчет числа каких-то объектов. В этом случае нужно завести словарь, в котором ключами являются объекты, а значениями — их количество. Хранение каких-либо данных, связанных с объектом. Ключи — объекты, значения — связанные с ними данные. Например, если нужно по названию месяца определить его порядковый номер, то это можно сделать при помощи словаря Num['January'] = 1; Num['February'] = 2; .... Установка соответствия между объектами (например, “родитель—потомок”). Ключ — объект, значение — соответствующий ему объект. Если нужен обычный массив, но при этом масимальное значение индекса элемента очень велико, но при этом будут использоваться не все возможные индексы (так называемый “разреженный массив”), то можно использовать ассоциативный массив для экономии памяти. Создание словаря Пустой словарь можно создать при помощи функции dict() или пустой пары фигурных скобок {} (вот почему фигурные скобки нельзя использовать для создания пустого множества). Для создания словаря с некоторым набором начальных значений можно использовать следующие конструкции: Capitals = {'Russia': 'Moscow', 'Ukraine': 'Kiev', 'USA': 'Washington'} Capitals = dict(Russia = 'Moscow', Ukraine = 'Kiev', USA = 'Washington') Capitals = dict([("Russia", "Moscow"), ("Ukraine", "Kiev"), ("USA", "Washington")]) Capitals = dict(zip(["Russia", "Ukraine", "USA"], ["Moscow", "Kiev", "Washington"])) Первые два способа можно использовать только для создания небольших словарей, перечисляя все их элементы. Кроме того, во втором способе ключи передаются как именованные параметры функции dict, поэтому в этом случае ключи могут быть только строками, причем являющимися корректными идентификаторами. В третьем и четвертом случае можно создавать большие словари, если в качестве аргументов передавать уже готовые списки, которые могут быть получены не обязательно перечислением всех элементов, а любым другим способом построены по ходу исполнения программы. В третьем способе функции dict нужно передать список, каждый элемент которого является кортежем из двух элементов: ключа и значения. В четвертом способе используется функция zip, которой передается два списка одинаковой длины: список ключей и список значений. Работа с элементами словаря Основная операция: получение значения элемента по ключу, записывается так же, как и для списков: A[key]. Если элемента с заданным ключом не существует в словаре, то возникает исключение KeyError. Другой способ определения значения по ключу — метод get: A.get(key). Если элемента с ключом get нет в словаре, то возвращается значение None. В форме записи с двумя аргументами A.get(key, val) метод возвращает значение val, если элемент с ключом key отсутствует в словаре. Проверить принадлежность элемента словарю можно операциями in и not in, как и для множеств. Для добавления нового элемента в словарь нужно просто присвоить ему какое-то значение: A[key] = value. Для удаления элемента из словаря можно использовать операцию del A[key] (операция возбуждает исключение KeyError, если такого ключа в словаре нет. Вот два безопасных способа удаления элемента из словаря. Способ с предварительной проверкой наличия элемента: if key in A: del A[key] Способ с перехватыванием и обработкой исключения: try: del A[key] except KeyError: pass Еще один способ удалить элемент из словаря: использование метода pop: A.pop(key). Этот метод возвращает значение удаляемого элемента, если элемент с данным ключом отсутствует в словаре, то возбуждается исключение. Если методу pop передать второй параметр, то если элемент в словаре отсутствует, то метод pop возвратит значение этого параметра. Это позволяет проще всего организовать безопасное удаление элемента из словаря: A.pop(key, None). 46 15.1 Перебор элементов словаря Можно легко организовать перебор ключей всех элементов в словаре: for key in A: print(key, A[key]) Следующие методы возвращают представления элементов словаря. Представления во многом похожи на множества, но они изменяются, если менять значения элементов словаря. Метод keys возвращает представление ключей всех элементов, метод values возвращает представление всех значений, а метод items возвращает представление всех пар (кортежей) из ключей и значений. Соответственно, быстро проверить, если ли значение val среди всех значений элементов словаря A можно так: val in A.values(), а организовать цикл так, чтобы в переменной key был ключ элемента, а в переменной val было его значение можно так: for key, val in A.items(): print(key, val)