Как разрезать большую задачу на куски с понятным входом и выходом — и как узнать, что часть этих кусков уже написана до нас. 10 класс, углублённый уровень — занятие девятое.
Два урока плана за одну пару: «Разбиение задачи на подзадачи» и «Использование стандартной библиотеки языка программирования. Подключение библиотек подпрограмм сторонних производителей». Тема одна: большая задача собирается из маленьких, и не все из них надо писать самому.
Писать такую программу «с начала и до конца» неудобно: непонятно, с чего начать, а когда она наконец напечатает неверное число, будет неясно, какая именно часть соврала.
Выход один: разрезать на подзадачи — куски, у каждого из которых есть свой вход и свой выход.
| Этап | Что на входе | Что на выходе |
|---|---|---|
| 1. граница перебора | N | целая часть корня из N |
| 2. перебор делителей | N и граница | счётчик и сумма |
| 3. вывод | счётчик и сумма | две строки на экране |
И сразу видно главное: первый этап уже кем-то написан — целый корень есть в библиотеке.
n = 48
s = n * 2
t = s + 3
print(t)
s нужен n, t нужен s,
печати нужен t. Переставить нельзя.
NameError: name 's' is not defined — так
Python говорит: этап попросил то, чего ещё никто не посчитал.А вот два независимых этапа переставляются свободно — это и есть проверка, связаны они данными или нет.
gr = isqrt(N)
print("граница:", gr) # временно: посмотрели и убрали
Написал этап — убедись, что он даёт то, что обещал, и только потом пиши следующий. Иначе ошибку придётся искать во всей программе сразу.
print убирают:
оболочка сверяет вывод целиком, и лишняя строка — уже другой ответ.| Цикл, который мы бы написали | Готовый вызов | Что это |
|---|---|---|
| вычитаем меньшее из большего, пока не сравняются | gcd(a, b) | НОД — наибольший общий делитель |
| ищем наибольшее d, у которого d · d ≤ n | isqrt(n) | целый квадратный корень |
| умножаем все числа от 2 до n | factorial(n) | факториал n! = 1 · 2 · … · n |
| если число отрицательное — меняем знак | abs(x) | модуль числа |x| |
| сравниваем два числа и берём большее | max(a, b) | наибольшее из чисел |
Своя реализация такого куска — это не «честнее». Это дольше, и в ней бывают ошибки, которых в библиотеке уже нет.
import math # пришло одно имя: math
print(math.sqrt(16)) # звать через точку
from math import sqrt, gcd # пришли два имени, а math — нет
print(sqrt(16)) # звать без приставки
from random import * # пришли все имена библиотеки
print(randint(1, 6))
NameError: name 'sqrt' is not defined почти
всегда значит: подключение из одной строчки, а вызов — из другой.Мы пишем from math import ... и
from random import * — как на доске.
| Модуль | Что берём сегодня |
|---|---|
math | sqrt, isqrt,
floor, ceil, gcd,
factorial, hypot, pi |
random | randint,
randrange, random, seed |
time | time() — сколько секунд считалась
программа |
«Стандартная» — значит, она уже внутри установленного Python: достаточно
import, скачивать нечего.
Что делает каждая функция — на следующих трёх слайдах.
math| Вызов | Что делает | Пример → результат |
|---|---|---|
sqrt(x) | квадратный корень (square root); ответ всегда дробный | sqrt(16) → 4.0 sqrt(2) → 1.414… |
isqrt(n) | целый квадратный корень (integer sqrt): наибольшее целое d, у которого d · d ≤ n | isqrt(16) → 4 isqrt(17) → 4 |
floor(x) | округление вниз, «пол» (floor) — к меньшему целому | floor(3.7) → 3 floor(-3.2) → -4 |
ceil(x) | округление вверх, «потолок» (ceiling) — к большему целому | ceil(3.2) → 4 ceil(-3.7) → -3 |
gcd(a, b) | НОД — наибольший общий делитель (greatest common divisor) | gcd(12, 18) → 6 gcd(0, 7) → 7 |
factorial(n) | факториал: n! = 1 · 2 · 3 · … · n | factorial(5) → 120 |
hypot(a, b) | гипотенуза (hypotenuse) по катетам a и b: √(a² + b²) | hypot(3, 4) →
5.0 |
pi | не функция, а число π — пишется без скобок | pi → 3.141592653589793 |
random и time| Вызов | Что делает | Пример → результат |
|---|---|---|
randint(a, b) | случайное целое от a до b, обе границы входят (random integer) | randint(1, 6) → одно из 1, 2, 3, 4, 5, 6 |
randrange(a, b) | случайное целое от a до b − 1:
правая граница не входит, как в range |
randrange(1, 6) → одно из 1 … 5 |
random() | случайное дробное от 0 до 1 (единица не выпадает); скобки пустые, но обязательны | random() → например 0.639… |
seed(x) | посев: задаёт, с какого места пойдёт последовательность. Один посев — одни и те же числа у всех | seed(42) → ничего не печатает |
time()из модуля time |
сколько секунд прошло с 1 января 1970 года. Само по себе число бесполезное — полезна разность двух замеров | t0 = time()…программа… print(time() - t0) → 0.84… |
Эти работают без import — они есть в Python всегда.
| Вызов | Что делает | Пример → результат |
|---|---|---|
abs(x) | модуль числа |x| (absolute): отбрасывает минус | abs(-5) → 5 |
max(a, b, …)min(a, b, …) |
наибольшее / наименьшее из перечисленных чисел | max(3, 8, 5) → 8min(3, 8, 5) → 3 |
int(x) | целое число: у дробного отбрасывает дробную часть, то есть округляет к нулю | int(3.9) → 3int(-3.9) → -3 |
round(x) | округление до ближайшего целого; ровная половина — к чётному | round(2.7) → 3round(2.5) → 2 |
help(f) | печатает справку о функции f: что принимает и что возвращает | help(round) |
print(sqrt(16)) # 4.0 — дробное, хоть и ровное
print(isqrt(16)) # 4 — целое
print(hypot(3, 4)) # 5.0 — дробное
print(gcd(12, 18)) # 6 — целое
4.0 — уже другой ответ. Целый корень — это
isqrt.print(int(-7 / 2)) # -3
print(-7 // 2) # -4
print(round(2.5)) # 2
print(round(3.5)) # 4
int округляет к нулю, // и
floor — вниз. round округляет ровную
половину к чётному.
from random import * строка seed = int(input())
занимает имя seed числом, и вызов seed(...)
падает. Входное число зовём start.Чужая функция делает то, что написано в её документации, а не то, чего мы от неё ждём.
N = 36. Делители парами: 1 и 36, 2 и 18, 3 и 12, 4 и 9, 6 и 6.
Всего 9 делителей, сумма 91.
Перебирать до 36 незачем: после корня пойдут те же пары наоборот.
gr = isqrt(N)
d = 1
while d <= gr:
if N % d == 0:
k = k + 2
s = s + d + N // d
if d * d == N:
k = k - 1
s = s - d
d = d + 1
g = 0
for i in range(1, K + 1):
x = int(input())
g = gcd(g, x)
print(g)
Почему старт — ноль: gcd(0, x) = x, и первое же число
попадает в накопитель целиком.
Старт с единицы всё ломает: gcd(1, x) всегда 1.
Готовая функция не избавляет от мысли: позвать её тоже надо правильно.
from random import *
seed(42)
print(randint(1, 6))
print(randint(1, 6))
seed(start) — посев: он задаёт, с какого места генератор
начнёт последовательность.
С одним и тем же посевом у всех выпадет одно и то же — поэтому такую программу вообще можно проверить.
randrange(1, 6) — не кубик: правая граница
не входит, шестёрка не выпадет никогда. randint(1, 6) — входит.>>> help(round)
round(number, ndigits=None)
Round a number to a given precision in decimal digits.
Это та же шапка «что на входе, что на выходе», которую мы писали сами в
прошлой паре, только написали её авторы Python. Наберите в IDLE
help(isqrt) — и не придётся гадать.
Стандартная приходит вместе с Python. Сторонние пишут другие люди — их сотни тысяч, и на компьютере их нет, пока не поставишь.
| Библиотека | Зачем |
|---|---|
pygame | игры: окно, картинки, звук |
pillow | обработка изображений |
requests | запросы в сеть |
numpy | быстрые вычисления над таблицами чисел |
pip по шагамcmd, Enter. Не IDLE!C:\Users\ученик> pip install pygame
Collecting pygame
Downloading pygame-2.6.1-cp313-cp313-win_amd64.whl (10.6 MB)
Successfully installed pygame-2.6.1
Successfully installed — готово. Теперь в
программе обычный import pygame.pip list — что уже установлено,
pip uninstall pygame — удалить.
import pygame →
ModuleNotFoundError: No module named 'pygame'. Программа ни при
чём: на этом компьютере такой библиотеки просто нет.C:\…>, IDLE — с
>>>. pip install в IDLE даёт
SyntaxError: это команда Windows, а не строчка Python."pip" не является внутренней или внешней командой — то же
самое через Python:py -m pip install pygamepip ставит то, что попросили, и за содержимое не
отвечает. Опечатка в имени (pygme вместо pygame)
может поставить подделку. Смотрят, кто автор, много ли пользователей и когда
библиотека обновлялась. На школьные машины сегодня не ставим — смотрим на
проекторе.isqrt, НОД цепочки через gcd,
броски кубика через seed и randint.Проверка.bat из общей папки:
девять наборов данных на каждое задание.Отметка: три задания — «5», два — «4», одно — «3». Попыток сколько угодно.
Сначала тренажёр, потом три программы.
Домашнее задание — на странице «Домашнее задание»: шесть типов задач на чтение программ и библиотек, Python дома не нужен. В начале следующей пары — проверочная по этим же типам.