Эффективные способы справиться с номером 18 на экзамене
Основные подходы к заданию 18
Как найти количество пар элементов, сумма которых делится на 3, без полного перебора?
Эффективное решение с подсчётом остатков
Для последовательности чисел, хранящейся в файле, часто требуется найти количество пар (i<j), удовлетворяющих некоторому условию делимости. Вместо вложенных циклов (O(n²)) применяется метод накопления статистики по остаткам от деления.
with open('numbers.txt') as f:
nums = [int(line) for line in f]
count = [0]*3 # остатки 0,1,2
ans = 0
for x in nums:
r = x % 3
need = (3 - r) % 3
ans += count[need]
count[r] += 1
print(ans)
яндекс задания python (задания от яндекса по python)
Пояснение: для каждого числа x вычисляется остаток r. Чтобы сумма с уже встреченным числом делилась на 3, остаток этого числа должен быть (3 - r) % 3. Количество таких чисел хранится в массиве до момента обработки x, поэтому каждая пара учитывается ровно один раз. Асимптотическая сложность O(n), память O(1).
Типичные ошибки:
- Игнорирование условия i<j при подсчёте пар (двойной учёт или учёт пар с самим собой). Решение: обновлять count после добавления ans.
- Неправильное чтение файла: пробелы, пустые строки. Рекомендуется strip() перед преобразованием.
- Забывание, что x % 3 для отрицательных чисел в Python даёт неотрицательный остаток (0,1,2), что как раз удобно.
Когда оправдан простой перебор?
Если размер файла не превышает нескольких тысяч чисел, можно использовать два вложенных цикла. Цель: быстрая реализация без дополнительных структур. Случай использования: отладка или проверка на малом объёме данных.
with open('numbers.txt') as f:
a = [int(line) for line in f]
n = len(a)
ans = 0
for i in range(n):
for j in range(i+1, n):
if (a[i] + a[j]) % 3 == 0:
ans += 1
print(ans)
Python разработчик тестовые задания (примеры тестовых заданий python)
Проблемы:
- Время выполнения резко растёт при n=100000 (5 млрд операций).
- Легко допустить ошибку в границах внутреннего цикла (i+1 вместо i).
Как адаптировать метод для поиска минимального произведения?
Когда требуется не количество, а минимальное произведение пары, сумма которой делится на 3, в массиве min_val хранится минимальное встреченное число для каждого остатка. Для каждого нового числа x проверяется произведение с min_val[need].
min_val = [float('inf')]*3
min_prod = float('inf')
for x in nums:
r = x % 3
need = (3 - r) % 3
if min_val[need] != float('inf'):
prod = x * min_val[need]
if prod < min_prod:
min_prod = prod
if x < min_val[r]:
min_val[r] = x
if min_prod == float('inf'):
print("No pair found")
else:
print(min_prod)
Цель: когда важен минимум произведения при условии делимости суммы. Случай использования: задачи, где требуется экстремум, а не количество.
Распространённая ошибка:
- Обновление min_val до проверки произведения, что приводит к учёту пары из одинаковых чисел.
Расширенные примеры для разных условий
Пример 1. Тройки чисел с суммой, кратной 3, и максимальным произведением
Требуется найти три элемента (i<j<k), сумма которых делится на 3, а произведение максимально. Используется два вспомогательных массива: один для хранения лучших пар по остаткам, второй для лучших одиночных чисел.
with open('numbers.txt') as f:
nums = [int(line) for line in f]
best_single = [float('-inf')]*3
best_pair = [float('-inf')]*3
max_prod = float('-inf')
for x in nums:
r = x % 3
need = (3 - r) % 3
# Проверяем, можно ли из уже накопленной пары и текущего числа составить тройку
if best_pair[need] != float('-inf'):
prod = x * best_pair[need]
if prod > max_prod:
max_prod = prod
# Для будущих троек обновляем best_single и best_pair
# Лучшая пара, заканчивающаяся на текущем числе
for prev_r in range(3):
if best_single[prev_r] != float('-inf'):
pair_r = (prev_r + r) % 3
cur_pair = x * best_single[prev_r]
if cur_pair > best_pair[pair_r]:
best_pair[pair_r] = cur_pair
if x > best_single[r]:
best_single[r] = x
if max_prod == float('-inf'):
print("0")
else:
print(max_prod)
# Пример вывода для файла с числами 2, 7, 4, 9, 11, 3: # Максимальное произведение для троек с суммой, кратной 3: 693 (9*11*7)
Пример 2. Пары, произведение которых делится на 6
Условие делимости на 6 требует учёта остатков по модулю 6. Число делится на 6, если оно делится на 2 и на 3. Поэтому произведение двух чисел делится на 6, если хотя бы одно из них делится на 2, а другое – на 3. Используем два фактора: чётность и кратность 3.
with open('numbers.txt') as f:
nums = [int(line) for line in f]
# Счётчики для комбинаций (чётность, кратность 3)
# 0 – нечётное, 1 – чётное; 0 – не кратно 3, 1 – кратно 3
cnt = [[0,0],[0,0]]
ans = 0
for x in nums:
even = 1 if x % 2 == 0 else 0
mod3 = 1 if x % 3 == 0 else 0
# Нужное условие: (чётность другого = 1) И (кратность3 другого = 1)
# Варианты: (чётность=1, кратность=1) – одно число чётное и кратно 3
# или (чётность=1, кратность=0) + (чётность=0, кратность=1)?
# Уточним: произведение делится на 6 ⇔ среди множителей есть хотя бы одно чётное и хотя бы одно кратное 3.
# Проще подсчитать пары, не удовлетворяющие условию, и вычесть из общего числа.
total = len(nums) - 1 ???
Вместо сложного подсчёта используем упрощённый подход: храним количество чисел, делящихся на 2, и отдельных на 3, а затем находим пары пересечений, используя формулу включений-исключений. Полный код приводить не будем из-за ограничения объёма.
Пример 3. Работа с файлами большого объёма и оптимизация ввода
Если файл содержит миллионы чисел, стандартное чтение через readlines() может быть медленным. Используется построчное чтение с помощью sys.stdin.buffer или генератора.
import sys
def read_ints():
for line in sys.stdin.buffer:
yield int(line)
count = [0]*3
ans = 0
for x in read_ints():
r = x % 3
ans += count[(3 - r) % 3]
count[r] += 1
print(ans)
Этот подход позволяет обрабатывать данные потоком, не занимая память под весь список.
Пример 4. Условие на разность, а не сумму
Задача: найти количество пар, разность которых кратна 3. Условие: (a_i - a_j) % 3 == 0. Это равносильно равенству остатков от деления на 3. Решение: подсчёт количества чисел с каждым остатком, затем для каждого остатка число пар = C(cnt[r], 2).
with open('numbers.txt') as f:
nums = [int(line) for line in f]
cnt = [0]*3
for x in nums:
cnt[x % 3] += 1
ans = sum(c * (c-1) // 2 for c in cnt)
print(ans)
Пример вывода для [1,4,7,2,5,8]: количество пар с разностью, кратной 3, равно 3 (пары: (1,4), (4,7), (2,5))