Что проверяет задание
Дана таблица: каждая строка, набор чисел. Нужно посчитать, сколько строк удовлетворяют логическому условию на повторы, суммы, средние, чётность или отношение величин.
Определения
Строка таблицы, один набор значений. Условие проверяют независимо для каждой строки, если не сказано иное.
Повторяющиеся значения, числа, которые встречаются в строке больше одного раза. Полезно отдельно смотреть, сколько различных чисел в строке и каковы их кратности.
Кратность, сколько раз конкретное число встретилось в строке.
Среднее арифметическое, сумма чисел, делённая на их количество.
Квадрат / произведение, часто сравнивают квадрат одного числа с произведением других, чтобы не извлекать корень.
Чётность, свойство делиться на 2. Для нескольких чисел смотрят, сколько среди них чётных.
Диапазон строки, разность между максимальным и минимальным числом.
Уникальный элемент, число с кратностью 1.
Ровно / хотя бы / не более, кванторы на количество. «Ровно одно повторяющееся» не то же самое, что «есть повторяющиеся».
Как не перепутать условие
Сложное условие почти всегда, конъюнкция или дизъюнкция нескольких свойств. Нужно сохранить связки: «и», «или», «либо … либо». Особенно осторожно с фразами:
- «все числа различны»;
- «есть ровно два одинаковых, остальные различны»;
- «самое большое число встречается один раз»;
- «сумма повторяющихся больше суммы неповторяющихся».
Перед подсчётом полезно переписать условие как набор предикатов над мультимножеством чисел строки.
Общий алгоритм решения
- Понять структуру файла: сколько чисел в строке, чем они разделены, есть ли заголовок.
- Формализовать условие через кратности, суммы, максимум, минимум и чётность.
- Для каждой строки вычислить нужные характеристики и проверить предикат.
- Посчитать, сколько строк предикат истинен.
- Если просят сумму подходящих строк, номер строки или значение в строке, не подменить это количеством.
- Проверить граничные случаи: все числа равны, два максимума, ноль, отрицательные числа.
Программа построчно
На экзамене задание 9 решают в электронной таблице формулами. Но то же самое быстрее и надёжнее сделать программой на Python: прочитать файл строка за строкой и посчитать строки, которые подходят под условие.
Разберём программу на условии:
В каждой строке файла 9.csv записаны натуральные числа. Определите количество строк, в которых все числа различны и наибольшее число меньше суммы остальных.
count = 0
for line in open("9.csv", encoding="utf-8"):
arr = list(map(int, line.split(";")))
if len(arr) == len(set(arr)) and max(arr) < sum(arr) - max(arr):
count += 1
print(count)
count = 0 - счётчик подходящих строк.
for line in open("9.csv", encoding="utf-8"): - открываем файл и перебираем его строки по одной. Каждая строка файла попадает в переменную line в виде текста, например '263;79;191\n'. Подробно - в разделе «Чтение файла».
arr = list(map(int, line.split(";"))) - превращаем текст строки в список чисел [263, 79, 191]. Эта строка разобрана по шагам в разделе «Чтение файла».
if … and …: - условие из задачи. Каждая часть, соединённая словом and, - один пункт условия: «все числа различны» и «наибольшее меньше суммы остальных».
count += 1 - строка подходит, увеличиваем счётчик.
print(count) - печатаем ответ после того, как просмотрены все строки.
Чтение файла
В задании 9 таблица дана в файле 9.csv - это обычный текст: каждая строка таблицы - строка вида 263;79;191, числа через ;. Такой файл открывается и в электронной таблице (LibreOffice Calc, Excel), и в программе.
Если таблица дана файлом электронной таблицы (9.xlsx, 9.ods - так бывает на экзамене), программе удобнее читать текст, поэтому таблицу сначала сохраняют как CSV:
- Откройте файл в LibreOffice Calc или Excel.
- «Файл → Сохранить как» (в Excel - «Сохранить как → Другие форматы»).
- Тип файла - «Текст CSV (.csv)», имя -
9.csv. - В окне параметров выберите разделитель полей «;» и сохраните. На вопрос о формате ответьте «Использовать формат CSV».
Файл из условия сохраняют в формате .csv (числа через ;) или .txt (числа через пробел) и кладут рядом с программой - тогда к нему можно обращаться просто по имени.
for line in open("9.csv", encoding="utf-8"):
arr = list(map(int, line.split(";")))
Как строка '263;79;191\n' превращается в список чисел:
line.split(";")- разрезает текст по точке с запятой:['263', '79', '191\n']. Получились строки, а не числа. Если числа в файле разделены пробелами, пишутline.split()без аргумента - он режет по любым пробелам и сам отбрасывает перевод строки.map(int, …)- применяетintк каждому куску:'263'→ 263,'191\n'→ 191 (intсам убирает перевод строки и пробелы по краям).list(…)- собирает результат в список:[263, 79, 191]. Список нужен, потому что дальше к числам обращаются по номеру и считают по нимsum,max,count.
encoding="utf-8" - в какой кодировке записан файл; для файлов только с цифрами её можно не указывать, но привычка полезная.
Если в файле в конце есть пустая строка, int('') вызовет ошибку. Её пропускают проверкой if not line.strip(): continue.
Подсчёт строк
count = 0
for line in open("9.csv", encoding="utf-8"):
arr = list(map(int, line.split(";")))
if условие:
count += 1
print(count)
Счётчик увеличивается только для строк, где условие истинно. Несколько условий соединяют словом and - строка подходит, только если выполнены все. Если в задаче «выполнено хотя бы одно из условий», используют or.
print(count) пишется без отступа - после цикла. С отступом число печаталось бы после каждой строки файла.
Сумма, максимум, среднее
sum(arr)- сумма всех чисел строки;max(arr),min(arr)- наибольшее и наименьшее;max(arr) - min(arr)- разность наибольшего и наименьшего («размах»);sum(arr) - max(arr)- сумма всех чисел, кроме наибольшего («сумма остальных»);sum(arr) / len(arr)- среднее арифметическое:len(arr)- количество чисел в строке;x % 5 == 0- число кратно 5 (остаток от деления на 5 равен нулю),x % 2 == 1- нечётное.
Сортировка строки
arr.sort()
sort() упорядочивает список по возрастанию прямо на месте: [263, 79, 191] → [79, 191, 263]. После сортировки arr[0] - наименьшее число, arr[-1] - наибольшее, arr[1] - среднее из трёх. Это удобно, когда условие говорит о «наибольшей стороне» или «двух меньших».
sorted(arr) делает то же, но возвращает новый список, а исходный не меняет.
Повторяющиеся числа
set(arr) - множество чисел строки: каждое значение хранится в нём один раз. Для [3, 7, 7, 2, 9, 1] множество - {1, 2, 3, 7, 9}, в нём 5 элементов, а в строке 6 чисел.
- «все числа различны» →
len(arr) == len(set(arr)): повторов нет, если множество не короче строки; - «ровно одно число повторяется дважды, остальные различны» (в строке из 6 чисел) →
len(set(arr)) == 5; arr.count(x)- сколько раз число x встречается в строке;[x for x in arr if arr.count(x) == 2]- список всех чисел, которые встречаются ровно 2 раза;[x for x in arr if arr.count(x) == 1]- неповторяющиеся.
Counter
from collections import Counter
c = Counter(arr)
Counter считает, сколько раз встречается каждое число строки. Для [5, 3, 5, 5, 8] получится Counter({5: 3, 3: 1, 8: 1}): пятёрка - 3 раза, тройка и восьмёрка - по разу.
c.values()- только количества: 3, 1, 1;- «какое-то число встречается ровно три раза» →
3 in c.values(); - «повторов нет» →
max(c.values()) == 1: даже самое частое число встречается один раз; c[5]- сколько раз встречается конкретное число 5.
any и all
any(x % 5 == 0 for x in arr)-True, если хотя бы одно число строки кратно 5;all(x % 2 == 1 for x in arr)-True, если все числа нечётные.
Внутри скобок - короткая запись цикла: «для каждого x из arr проверить условие». Это то же самое, что цикл с флажком, только в одну строку.
Геометрия в строках
Если три числа строки - длины сторон, сначала их сортируют: arr.sort(), тогда arr[2] - наибольшая сторона.
- Треугольник существует (неравенство треугольника), если сумма двух меньших сторон больше наибольшей:
arr[0] + arr[1] > arr[2]. Для других пар сторон неравенство выполняется автоматически, поэтому достаточно одной проверки. - Остроугольный треугольник: квадрат наибольшей стороны меньше суммы квадратов двух других -
arr[0] ** 2 + arr[1] ** 2 > arr[2] ** 2(**- возведение в степень). Равенство - прямоугольный, обратный знак - тупоугольный. - Параллелепипед с рёбрами a ≤ b ≤ c: площади граней -
a * b,a * c,b * c, наибольшая из нихb * c. «Площадь наибольшей грани больше суммы двух других»:arr[0] * arr[1] + arr[0] * arr[2] < arr[1] * arr[2].
Оставшиеся числа
Условия часто говорят об «оставшихся», «остальных» или «других» числах: «квадрат суммы максимального и минимального больше суммы квадратов трёх оставшихся». Удобнее всего работать с отсортированной копией строки:
s = sorted(arr)
Для строки [40, 69, 77, 14, 25] получится s = [14, 25, 40, 69, 77]. Теперь у каждого числа своё место:
s[0]- наименьшее (минимальное) число,s[-1]- наибольшее (максимальное);s[-2]- второе по величине,s[1]- второе с начала;s[1:-1]- все числа, кроме наименьшего и наибольшего:[25, 40, 69];s[:-1]- все, кроме наибольшего («наибольшее меньше суммы трёх других» →s[-1] < sum(s[:-1]));s[-2:]- два наибольших числа,s[:-2]- всё остальное;s[:3]- три наименьших,s[3:]- остальные.
Срез s[a:b] берёт элементы с номера a до номера b, не включая b; отрицательный номер считается с конца. Пример условия целиком:
s = sorted(arr)
if (s[0] + s[-1]) ** 2 > sum(x ** 2 for x in s[1:-1]):
count += 1
sum(x ** 2 for x in s[1:-1]) - сумма квадратов оставшихся чисел: для каждого x из среза считаем x ** 2 и складываем. sorted не меняет arr: порядок чисел в самой строке остаётся прежним (он нужен, если условие говорит о первом и последнем числе или о порядке).
Картина повторов
Условия вида «в строке одно число повторяется трижды, остальные четыре числа различны» удобно проверять одной строкой. Counter(arr) считает, сколько раз встречается каждое число, а c.values() - только эти количества:
from collections import Counter
c = Counter(arr)
if sorted(c.values()) == [1, 1, 1, 1, 3]:
count += 1
Для строки [5, 8, 5, 2, 9, 5, 4] получим c = Counter({5: 3, 8: 1, 2: 1, 9: 1, 4: 1}), sorted(c.values()) = [1, 1, 1, 1, 3]: одно число трижды и четыре числа по одному разу. Так записываются все подобные условия (в строке из 7 чисел):
- «два числа повторяются дважды, остальные три различны» →
[1, 1, 1, 2, 2]; - «одно число трижды, одно дважды, остальные без повторений» →
[1, 1, 2, 3]; - «ровно три числа повторяются дважды и одно без повторений» →
[1, 2, 2, 2].
Сумма чисел в списке всегда равна количеству чисел в строке - так удобно проверять себя. Если условие говорит только «все числа различны», хватит len(set(arr)) == len(arr).
Повторяющиеся и неповторяющиеся
Когда в условии сравниваются суммы или средние повторяющихся и неповторяющихся чисел, заведите два списка:
rep = [x for x in arr if arr.count(x) > 1] # повторяющиеся (каждое столько раз, сколько встречается)
uniq = [x for x in arr if arr.count(x) == 1] # неповторяющиеся
Для [5, 8, 5, 2, 9, 5, 4]: rep = [5, 5, 5], uniq = [8, 2, 9, 4]. Дальше условия пишутся почти дословно:
- «среднее арифметическое неповторяющихся не больше повторяющегося числа» →
sum(uniq) / len(uniq) <= rep[0]; - «утроенный квадрат повторяющегося числа больше суммы квадратов неповторяющихся» →
3 * rep[0] ** 2 > sum(x ** 2 for x in uniq); - «максимальное неповторяющееся число» →
max(uniq).
rep[0] - само повторяющееся число, если оно в строке одно. Если повторяющихся чисел может не быть, перед делением проверяют len(rep) > 0 and …: иначе sum(rep) / len(rep) - деление на ноль.
Чётные и нечётные
even = [x for x in arr if x % 2 == 0] # чётные
odd = [x for x in arr if x % 2 != 0] # нечётные
- «чётных чисел больше, чем нечётных» →
len(even) > len(odd); - «сумма чётных чисел равна сумме нечётных» →
sum(even) == sum(odd); - «в строке есть как чётные, так и нечётные числа» →
len(even) > 0 and len(odd) > 0.
Так же собирают положительные (x > 0) и отрицательные (x < 0) числа. Для отрицательных x % 2 в Python тоже 0 или 1, а последнюю цифру числа берут как abs(x) % 10.
Порядок чисел в строке
Иногда важен порядок, в котором числа записаны:
if all(arr[i] < arr[i + 1] for i in range(len(arr) - 1)): # строго по возрастанию
count += 1
range(len(arr) - 1) перебирает номера всех соседних пар, arr[i] < arr[i + 1] - каждое следующее больше предыдущего. «В порядке неубывания» (соседние могут быть равны) проще: arr == sorted(arr). «По убыванию» - знак >, «невозрастания» - arr == sorted(arr, reverse=True). Здесь нельзя заменять arr на s: отсортированная копия всегда возрастает.
Хотя бы одно, ровно одно
Если условия соединены не словом «и», каждое удобно записать в свой флажок:
p1 = len(set(arr)) == len(arr)
p2 = s[-1] > sum(s[:-1])
if p1 != p2:
count += 1
- «выполнены оба (все) условия» →
p1 and p2; - «выполнено хотя бы одно» →
p1 or p2; - «выполнено ровно (только) одно из двух» →
p1 != p2: флажки разные - значит, истинен ровно один; - для трёх условий «ровно одно» →
[p1, p2, p3].count(True) == 1, «не более одного» →[p1, p2, p3].count(True) <= 1.
Номер строки и сумма строки
Если спрашивают не количество, а номер строки, перебирают строки вместе с номерами:
result = []
for n, line in enumerate(open("9.csv", encoding="utf-8"), start=1):
arr = list(map(int, line.split(";")))
if условие:
result.append(n)
print(max(result))
enumerate(…, start=1) выдаёт пары «номер, строка», нумерация - с 1, как в таблице. В список result попадают номера подходящих строк по порядку:
- наибольший номер -
max(result), наименьший -min(result); - сумма номеров -
sum(result); - номер 3-й сверху подходящей строки -
result[2](в Python счёт с нуля).
Если нужна сумма чисел строки, копят суммы: result.append(sum(arr)). Тогда строка с наименьшим номером - первая в списке, result[0], с наибольшим - последняя, result[-1].
Копирование формул
При копировании формулы адреса в ней сдвигаются на столько же столбцов и строк, на сколько сдвинулась сама ячейка. Знак $ закрепляет часть адреса:
B2- сдвигается и столбец, и строка;$B2- столбец закреплён, меняется только строка;B$2- строка закреплена, меняется только столбец;$B$2- адрес не меняется.
Пример: формулу =B$2+$D2 из C3 копируют в D4 - на 1 столбец вправо и на 1 строку вниз. B$2 → C$2 (столбец сдвинулся, строка закреплена), $D2 → $D3 (столбец закреплён, строка сдвинулась). Новая формула =C$2+$D3; её значение считают по числам в этих ячейках.
Таблица с датами
В задачах ФИПИ про погоду таблица хранит дробные числа и даты. Файл CSV из задания читается так же, построчно, но с тремя отличиями:
vals = []
for line in open("9.csv", encoding="cp1251"):
parts = line.strip().split(";")
if parts[0][3:5] != "06": # дата вида 01.06.2018: символы 3-4 - номер месяца
continue
t = [float(x.replace(",", ".")) for x in parts[1:25]]
vals.append(t[10] - t[9])
print(int(max(vals)))
- Кодировка файла -
cp1251(в заголовке русские буквы). - Дробная часть записана через запятую:
"13,7". Передfloatзапятую меняют на точку. - Первая строка - заголовок, у неё нет даты; её, как и строки других месяцев, пропускают через
continue.
В таблице ежечасных измерений после даты идут 24 числа: t[0] - температура в 0:00, t[9] - в 9:00. «С 5:00 до 8:00» - срез t[5:9] (правая граница среза не входит). В ответ часто просят целую часть: int(…).
