Каппес Даниил Олегович

ПРАКТИКУМ PYTHON · ЗАНЯТИЕ 4

range целиком, счётчики и цикл while

Цикл учится считать с любого места и в любую сторону, а рядом появляются приёмы, которые дальше встретятся всюду: счётчик, сумматор, флажок.

Чему научимся

range с одним, двумя и тремя параметрами

range(n) создаёт последовательность 0, 1, 2, …, n−1, а цикл for перебирает её по очереди. Но начинать не с нуля тоже можно — тогда параметров два:

  • range(n) — числа 0, 1, 2, …, n−1;
  • range(n, m) — числа n, n+1, …, m−1;
  • range(n, m, k) — то же, но с шагом k.

Правая граница никогда не включается. range(1, 5) — это 1, 2, 3, 4; чтобы дойти до пятёрки, пишут range(1, 6). Если первый параметр больше второго, последовательность получается пустой, и цикл не выполнится ни разу.

for i in range(1, 10, 2):
    print(i, end=' ')

Результат

1 3 5 7 9

Шаг может быть отрицательным — тогда последовательность убывает. Но старт при этом обязан быть больше конца, иначе она снова окажется пустой:

for i in range(5, 0, -1):
    print(i, end=' ')
print('Взлетаем!!!')

Результат

5 4 3 2 1 Взлетаем!!!

range() работает только с целыми числами, а шаг не может быть нулём: range(1, 10, 0) останавливает программу с ошибкой ValueError: range() arg 3 must not be zero.

4.1

Все числа от m до n

Даны два целых числа m и n (m ≤ n). Напишите программу, которая выводит все числа от m до n включительно.

Формат входных данных
Два целых числа m и n, каждое на отдельной строке.
Формат выходных данных
Числа от m до n, каждое на отдельной строке.
Ввод
1
4
Вывод
1
2
3
4
Подсказка 1

Начало последовательности — первый параметр range(), конец — второй.

Подсказка 2

Правая граница не включается, поэтому конец надо взять на единицу больше.

Разбор
  1. Читаем m и n.
  2. range(m, n + 1) даёт все числа включительно.
  3. Печатаем каждое в теле цикла.
Решение
m = int(input())
n = int(input())

# n + 1, потому что правая граница в range не включается
for i in range(m, n + 1):
    print(i)

Единица в n + 1 — самая частая описка первых недель. Проверять её удобно на одинаковых числах: при m = n = 4 программа обязана напечатать ровно одну строку.

4.2

В обе стороны

Даны два целых числа m и n. Напишите программу, которая выводит все числа от m до n включительно в порядке возрастания, если m < n, или в порядке убывания в противном случае.

Формат входных данных
Два целых числа m и n, каждое на отдельной строке.
Формат выходных данных
Числа от m до n, каждое на отдельной строке.
Ввод 1
1
4
Вывод 1
1
2
3
4
Ввод 2
4
1
Вывод 2
4
3
2
1
Подсказка 1

Случая два, и в каждом свой range(). Значит, нужна развилка.

Подсказка 2

При убывании шаг −1, а конец берётся на единицу меньше: range(m, n - 1, -1).

Разбор
  1. Сравниваем m и n.
  2. Если m < n — обычный range(m, n + 1).
  3. Иначе идём назад: range(m, n - 1, -1).
Решение
m = int(input())
n = int(input())

if m < n:
    for i in range(m, n + 1):
        print(i)
else:
    # шаг -1, и конец сдвигаем на единицу в другую сторону
    for i in range(m, n - 1, -1):
        print(i)

Обратите внимание: в обеих ветках граница сдвигается на единицу, но в разные стороны. Правило одно — «конец не включается», а вот куда от него шагнуть, зависит от направления.

4.3

Нечётные по убыванию

Даны два целых числа m и n (m > n). Напишите программу, которая выводит все нечётные числа от m до n включительно в порядке убывания.

При желании попробуйте решить задачу двумя способами: с условным оператором и без него.

Формат входных данных
Два целых числа m и n (m > n), каждое на отдельной строке.
Формат выходных данных
Нечётные числа от m до n по убыванию, каждое на отдельной строке.
Ввод
10
1
Вывод
9
7
5
3
1
Подсказка 1

Проще всего пройти все числа подряд и печатать не все.

Подсказка 2

Нечётное — это то, у которого остаток от деления на 2 не равен нулю.

Разбор
  1. Идём по всем числам от m до n с шагом −1.
  2. Внутри цикла проверяем i % 2 != 0 и печатаем только такие.
Решение
m = int(input())
n = int(input())

for i in range(m, n - 1, -1):
    if i % 2 != 0:      # печатаем только нечётные
        print(i)

Способ без развилки — начать с ближайшего нечётного и шагать по два: range(m - (m + 1) % 2, n - 1, -2). Работает, но выражение для старта надо расшифровывать; вариант с if понятнее, а быстрее ровно вдвое — на глаз это незаметно.

Счётчик, сумматор и произведение

Одна из самых частых задач — подсчёт количества. Держится она на переменной-счётчике: перед циклом её обнуляют, а в теле увеличивают на единицу, когда случилось то, что считаем.

counter = 0
for i in range(10):
    num = int(input())
    if num > 10:
        counter = counter + 1
print('Было введено', counter, 'чисел, больших 10.')

Строка counter = counter + 1 читается так: «возьми старое значение, прибавь единицу и положи обратно». Начальное значение обязательно: без него на первом же шаге Python не будет знать, к чему прибавлять.

Ту же строку часто пишут короче — counter += 1. Это одно и то же.

Сумма устроена так же, только прибавляется не единица, а само число:

total = 0
for i in range(10):
    num = int(input())
    if num > 10:
        total = total + num
print('Сумма чисел больших 10 равна', total)

У произведения начальное значение — единица, а не ноль: с нуля произведение так нулём и останется.

Через сумму считается и среднее значение — нужно только поделить накопленное на количество:

total = 0
for i in range(10):
    total += int(input())

average = total / 10
print('Среднее значение равно', average)

Наибольшее и наименьшее ищут тем же приёмом: заводят переменную и переприсваивают её, когда встретилось что-то больше (или меньше) уже найденного.

largest = -1
for i in range(10):
    num = int(input())
    if num > largest:
        largest = num
print('Наибольшее число равно', largest)

Начальное значение -1 здесь не случайно: в условии сказано, что все числа положительные, поэтому первое же сравнение сработает. Если про знак чисел ничего не известно, начальным берут первое прочитанное число, а не выдуманное.

И ещё один приём — сигнальная метка (флажок). Это переменная со значением True или False, которой одна часть программы сообщает другой, случилось ли что-нибудь. Классический пример — проверка числа на простоту:

num = int(input())
flag = True
for i in range(2, num):
    if num % i == 0:      # нашёлся делитель, кроме 1 и самого числа
        flag = False

if num == 1:
    print('Это единица, она не простая и не составная')
elif flag == True:
    print('Число простое')
else:
    print('Число составное')

Флажок ставят в True до цикла — «пока ничего плохого не нашли», — а в теле сбрасывают. После цикла по нему и принимают решение.

4.4

Количество чисел

На вход подаются два целых числа a и b (a ≤ b). Напишите программу, которая подсчитывает количество чисел в диапазоне от a до b включительно, куб которых оканчивается на 4 или 9.

Формат входных данных
Два целых числа a и b, каждое на отдельной строке.
Формат выходных данных
Одно целое число — количество.
Ввод 1
1
10
Вывод 1
2
Ввод 2
1
100
Вывод 2
20
Подсказка 1

«Оканчивается на 4» — это последняя цифра, то есть остаток от деления на 10.

Подсказка 2

Считать надо не числа, а совпадения: счётчик увеличивается только внутри развилки.

Разбор
  1. Заводим счётчик и обнуляем его до цикла.
  2. Перебираем все числа от a до b включительно.
  3. Для каждого считаем куб и берём его последнюю цифру: i ** 3 % 10.
  4. Если она равна 4 или 9 — увеличиваем счётчик.
Решение
a = int(input())
b = int(input())

count = 0
for i in range(a, b + 1):
    last = i ** 3 % 10        # последняя цифра куба
    if last == 4 or last == 9:
        count += 1

print(count)

Отдельная переменная last не обязательна, но без неё куб пришлось бы считать дважды в одном условии. Промежуточное имя часто и есть вся разница между «понятно» и «непонятно».

4.5

Сумма чисел

На вход подаётся натуральное число n, а затем n целых чисел, каждое на отдельной строке. Напишите программу, которая подсчитывает их сумму.

Формат входных данных
Число n, затем n целых чисел.
Формат выходных данных
Сумма введённых чисел.
Ввод
4
3
2
0
-1
Вывод
4
Подсказка 1

Сначала читается количество, и только потом — сами числа.

Подсказка 2

Накопитель обнуляется до цикла, а внутри к нему прибавляется очередное число.

Разбор
  1. Читаем n — сколько чисел будет.
  2. Обнуляем сумму.
  3. n раз читаем число и прибавляем к сумме.
  4. Печатаем после цикла — внутри печатались бы промежуточные суммы.
Решение
n = int(input())

total = 0
for i in range(n):
    total += int(input())

print(total)

Место команды print() здесь важнее самого сложения: строка с отступом попадёт в тело цикла и напечатается n раз. Ответ будет в последней строке — но задача просит одно число.

4.6

Асимптотическое приближение

На вход подаётся натуральное число n. Напишите программу, которая вычисляет значение выражения

Выражение

1/1 + 1/2 + 1/3 + … + 1/n − ln(n)

Натуральный логарифм даёт функция log() из модуля math.

Формат входных данных
Натуральное число n.
Формат выходных данных
Одно число — значение выражения.
Ввод 1
10
Вывод 1
0.6263831609742079
Ввод 2
1
Вывод 2
1.0
Подсказка 1

Сумма дробей — обычный накопитель, только прибавляется 1 / i.

Подсказка 2

Логарифм вычитается один раз, после цикла, а не на каждом шаге.

Разбор
  1. Копим сумму 1/1 + 1/2 + … + 1/n в цикле по range(1, n + 1).
  2. После цикла вычитаем math.log(n).
  3. Печатаем результат.
Решение
import math

n = int(input())

total = 0
for i in range(1, n + 1):
    total += 1 / i          # обычное деление: сумма дробная

print(total - math.log(n))  # логарифм вычитаем один раз, после цикла

Цикл начинается с единицы, а не с нуля: 1 / 0 остановило бы программу. Само выражение с ростом n стремится к постоянной Эйлера — примерно 0,5772; при n = 1 000 000 это уже видно в третьем знаке.

4.7

Факториал

На вход подаётся натуральное число n (n ≤ 12). Напишите программу, которая вычисляет n!.

Факториал натурального числа n — это произведение всех натуральных чисел от 1 до n.

Формат входных данных
Натуральное число n (n ≤ 12).
Формат выходных данных
Одно число — значение n!.
Ввод 1
5
Вывод 1
120
Ввод 2
1
Вывод 2
1
Подсказка 1

Это тот же накопитель, что и для суммы, только с умножением.

Подсказка 2

Начальное значение произведения — единица.

Разбор
  1. Заводим product = 1.
  2. Перебираем числа от 1 до n включительно и умножаем накопитель на каждое.
  3. Печатаем результат после цикла.
Решение
n = int(input())

product = 1                 # у произведения начало — единица, не ноль
for i in range(1, n + 1):
    product *= i

print(product)

В модуле math есть готовая factorial(), и она короче. Но смысл задачи — написать накопитель самому: тот же приём дальше понадобится там, где готовой функции не будет.

4.8

Сумма делителей

На вход подаётся натуральное число n. Напишите программу, которая выводит последовательно каждый делитель числа n, а затем итоговую сумму всех делителей.

Формат входных данных
Натуральное число n.
Формат выходных данных
Делители, каждый на отдельной строке, затем строка вида «Сумма делителей: 6».
Ввод 1
5
Вывод 1
1
5
Сумма делителей: 6
Ввод 2
12
Вывод 2
1
2
3
4
6
12
Сумма делителей: 28
Подсказка 1

Делитель — это число, на которое n делится без остатка.

Подсказка 2

Проверять надо все числа от 1 до самого n: и единица, и само число — тоже делители.

Разбор
  1. Перебираем d от 1 до n включительно.
  2. Если n % d == 0 — печатаем d и прибавляем его к сумме.
  3. После цикла печатаем итог с подписью.
Решение
n = int(input())

total = 0
for d in range(1, n + 1):
    if n % d == 0:      # d делит n нацело — значит, делитель
        print(d)
        total += d

print('Сумма делителей:', total)

Печать и накопление стоят рядом, в одной ветке: делитель нашёлся — значит, и показать его надо, и учесть. Разносить эти два действия по разным циклам незачем.

4.9

Все ли чётные

Напишите программу, которая считывает пять целых чисел и определяет, все ли они чётные. Программа выводит «YES», если все числа чётные, и «NO» в ином случае.

Формат входных данных
Пять целых чисел, каждое на отдельной строке.
Формат выходных данных
YES или NO.
Ввод 1
2
4
6
8
10
Вывод 1
YES
Ввод 2
2
4
7
8
10
Вывод 2
NO
Подсказка 1

Печатать ответ внутри цикла нельзя: пока не прочитаны все пять чисел, ответа ещё нет.

Подсказка 2

Нужен флажок: до цикла — «пока всё хорошо», внутри — сбросить, если встретилось нечётное.

Разбор
  1. Ставим all_even = True до цикла.
  2. Читаем пять чисел; встретилось нечётное — переводим флажок в False.
  3. После цикла печатаем ответ по значению флажка.
Решение
all_even = True             # пока не встретилось нечётное — считаем, что все чётные

for i in range(5):
    num = int(input())
    if num % 2 != 0:
        all_even = False

if all_even:
    print('YES')
else:
    print('NO')

Флажок обратно в True не возвращается никогда — в этом и смысл: одного нечётного числа достаточно, чтобы ответ стал «NO». На следующем занятии появится break, и цикл можно будет прерывать сразу после находки.

Цикл while: повторять, пока

for хорош, когда число повторений известно заранее. Но часто повторять надо до наступления события, и сколько шагов на это уйдёт — не угадать. Тогда берут while.

i = 0
while i < 10:
    print('Привет')
    i = i + 1

Условие проверяется перед каждым повторением, поэтому while называют циклом с предусловием. Если условие ложно с самого начала, тело не выполнится ни разу. Само слово while переводится как «пока» — цикл и работает, пока условие истинно.

Одно выполнение тела цикла называют итерацией. Это слово встретится и дальше: «на каждой итерации», «перейти к следующей итерации».

Вот задача, которую через for не написать: считывать числа и печатать их квадраты, пока не введут −1.

num = int(input())
while num != -1:
    print('Квадрат вашего числа равен:', num * num)
    num = int(input())

Здесь важны ровно две вещи: переменная должна получить начальное значение до цикла и обязана меняться внутри. Стоит забыть второе — и цикл станет бесконечным.

Бесконечный цикл — не редкость и не всегда ошибка (программа-обработчик запросов так и живёт), но в учебных задачах это почти всегда забытая строка. Остановить его можно сочетанием Ctrl+C в консоли.

Отдельный частый приём — чтение до стоп-значения. Программа читает данные, пока не встретит условленное слово или число:

text = input()
total = 0
while text != 'stop':
    total += int(text)
    text = input()
print('Сумма чисел равна', total)

Обратите внимание на порядок: сначала прочитали, потом проверили, и только потом обработали. Если поменять местами обработку и проверку, стоп-слово попадёт в подсчёт и программа остановится с ошибкой на int('stop').

4.10

До слова КОНЕЦ

На вход подаётся последовательность слов, каждое на отдельной строке. Концом последовательности является слово «КОНЕЦ». Напишите программу, которая выводит члены данной последовательности.

Само слово «КОНЕЦ» и всё, что идёт после него, выводить не нужно.

Формат входных данных
Слова, каждое на отдельной строке; последнее — «КОНЕЦ».
Формат выходных данных
Те же слова до стоп-слова, каждое на отдельной строке.
Ввод
Fus
Ro
КОНЕЦ
Dah
Вывод
Fus
Ro
Подсказка 1

Сколько будет слов, заранее неизвестно — значит, while.

Подсказка 2

Первое слово надо прочитать до цикла, иначе проверять будет нечего.

Разбор
  1. Читаем первое слово.
  2. Пока оно не равно «КОНЕЦ» — печатаем его и читаем следующее.
  3. Как только пришло стоп-слово, условие становится ложным и цикл заканчивается.
Решение
word = input()              # первое слово читаем до цикла

while word != 'КОНЕЦ':
    print(word)
    word = input()          # без этой строки цикл станет бесконечным

Две команды input() в такой программе — не дублирование, а устройство приёма: одна даёт первое значение для проверки, вторая — все следующие.

4.11

Количество членов

На вход подаётся последовательность слов, каждое на отдельной строке. Концом последовательности является одно из трёх слов: «стоп», «хватит», «достаточно» (маленькими буквами). Напишите программу, которая выводит общее количество членов последовательности.

Формат входных данных
Слова, каждое на отдельной строке; последнее — одно из трёх стоп-слов.
Формат выходных данных
Одно число — сколько слов было до стоп-слова.
Ввод
Skyrim
GTA
Mafia
стоп
Battlefield
Вывод
3
Подсказка 1

Стоп-слов три, и любое из них заканчивает работу. Значит, продолжать надо, только если слово не совпало ни с одним.

Подсказка 2

«Ни одно из трёх» — это три сравнения через and.

Разбор
  1. Заводим счётчик и читаем первое слово.
  2. Пока слово не равно ни одному из трёх стоп-слов — увеличиваем счётчик и читаем следующее.
  3. Печатаем счётчик после цикла.
Решение
count = 0
word = input()

# продолжаем, пока слово не совпало ни с одним из стоп-слов
while word != 'стоп' and word != 'хватит' and word != 'достаточно':
    count += 1
    word = input()

print(count)

Здесь легко перепутать and и or. С or цикл прекратится сразу: слово «Skyrim» не равно «стоп» — условие уже истинно... но и «хватит» оно тоже не равно, и цикл не остановится никогда. Проверяйте такие условия на одном шаге в уме.

4.12

Количество пятёрок

На вход подаётся последовательность целых чисел от 1 до 5 — оценки ученика, каждая на отдельной строке. Концом последовательности является любое отрицательное число либо число больше 5. Напишите программу, которая выводит количество пятёрок.

Формат входных данных
Числа, каждое на отдельной строке; последнее — вне диапазона оценок.
Формат выходных данных
Одно число — количество пятёрок.
Ввод
1
3
4
5
5
5
5
-17
2
5
Вывод
4
Подсказка 1

Условие продолжения — «число ещё похоже на оценку», то есть лежит в допустимых границах.

Подсказка 2

Внутри цикла нужна своя проверка: считаем не все числа, а только пятёрки.

Разбор
  1. Читаем первое число до цикла.
  2. Пока оно в границах оценок — проверяем, пятёрка ли, и читаем следующее.
  3. Печатаем счётчик после цикла.
Решение
count = 0
mark = int(input())

while 0 <= mark <= 5:       # вышли за границы оценок — конец последовательности
    if mark == 5:
        count += 1
    mark = int(input())

print(count)

Условие цикла — это перевёрнутое условие остановки: «отрицательное или больше пяти» превращается в «от нуля до пяти». Так короче и труднее ошибиться, чем писать while mark >= 0 and mark <= 5... хотя это ровно то же самое.

4.13

Ведьмаку заплатите чеканной монетой

Ведьмак способен одолеть любых чудовищ, но услуги его недёшевы, и купюр он не принимает — только чеканные монеты. В мире ведьмака есть монеты номиналом 1, 5, 10 и 25.

Напишите программу, которая определяет, какое минимальное количество монет нужно заплатить ведьмаку.

Формат входных данных
Одно натуральное число — цена за услугу.
Формат выходных данных
Минимально возможное количество монет.
Ввод 1
49
Вывод 1
7
Ввод 2
100
Вывод 2
4
Подсказка 1

Чтобы монет было меньше, брать надо самые крупные — сколько поместится.

Подсказка 2

«Сколько поместится» — это целочисленное деление, а «что осталось» — остаток. Дальше то же самое с монетой помельче.

Разбор
  1. Сколько раз в цене помещается 25 — столько монет по 25; остаток идёт дальше.
  2. То же с десятками, потом с пятёрками.
  3. Всё, что осталось, добираем монетами по одному.
Решение
price = int(input())

coins = price // 25         # сколько монет по 25
price = price % 25          # и что осталось после них

coins += price // 10
price = price % 10

coins += price // 5
price = price % 5

coins += price              # остаток добираем монетами по одному
print(coins)

Приём «бери самую крупную, сколько влезет» называется жадным, и здесь он даёт настоящий минимум — не при любом наборе монет это так. Когда на седьмом занятии появятся списки, четыре повторяющихся куска свернутся в короткий цикл по номиналам.

Итог занятия

Главное с четвёртого занятия. range() задаётся началом, концом и шагом, и правая граница не включается никогда. Счётчик, сумматор и произведение — один и тот же приём: обнулить (или поставить единицу) до цикла, менять внутри, печатать после. Флажок сообщает после цикла о том, что случилось внутри. while повторяет, пока условие истинно, и требует двух вещей: начального значения до цикла и изменения внутри.