Перейти к содержанию

412 уроков, 25 библиотек и челлендж «Что выведет код?» — бесплатно, код прямо в браузере

Начать обучение
Урок 18 из 20 Начальный 15 мин 100 XP

Задачи на множества Python: практика с решениями

Четыре задачи на множества из жизни данных: уникальные посетители журнала, общие друзья, первый неповторяющийся элемент и гости, пришедшие оба дня. Код, вывод и разбор каждого шага.

Редакция Питоники

Восемнадцать уроков строили инструментарий: уникальность из урока 1, мгновенный in из урока 5, пересечение и разность из уроков 7 и 8. Пора поработать этим инструментом по назначению. Четыре задачи ниже — из практики данных: журнал визитов, круги общения, поиск первого одиночки, сравнение двух дней. Каждая решается множествами за несколько строк, и в каждой разбор объясняет, почему именно эта операция, а не цикл с флажками.

Задача 1: уникальные посетители журнала

Журнал содержит запись на каждый визит, поэтому имена повторяются. Требуется ответить на три вопроса: сколько было визитов, сколько разных гостей и кто именно. Множество отвечает на вопрос «кто вообще приходил» за один вызов: set(log) собирает уникальные имена, len(set(log)) считает их.

решение: set и len
log = ['Анна', 'Борис', 'Анна', 'Вера', 'Анна', 'Глеб', 'Борис']

unique = set(log)
print('визитов:', len(log))
print('уникальных гостей:', len(unique))
print(sorted(unique))
Вывод
визитов: 7
уникальных гостей: 4
['Анна', 'Борис', 'Вера', 'Глеб']

Три вызова — три ответа. Цикл с флажком «уже видели» сделал бы то же самое, но занял бы пять строк; set делает это тем, что уникальность — его свойство, а не алгоритм. Печать через sorted — правило из урока 2: множество печатаем отсортированным, чтобы вывод не менялся между запусками.

Задача 2: общие друзья двух людей

У Анны и Бориса свои списки друзей, и надо найти пересечение — людей, которые дружат с обоими, — а заодно кандидатов в знакомые: друзей Анны, которых у Бориса нет. Это прямые операции из уроков про пересечение и разность:

решение: & и -
anna_friends = {'Вера', 'Глеб', 'Дана', 'Жанна'}
boris_friends = {'Вера', 'Глеб', 'Егор'}

common = anna_friends & boris_friends
suggest = anna_friends - boris_friends

print('общих друзей:', len(common))
print(sorted(common))
print('их нет у Бориса:', sorted(suggest))
Вывод
общих друзей: 2
['Вера', 'Глеб']
их нет у Бориса: ['Дана', 'Жанна']

Два знака вместо двух вложенных циклов. Пересечение & из урока 7 собирает тех, кто есть в обоих множествах; разность - из урока 8 — тех, кто есть у Анны и отсутствует у Бориса. Вложенные циклы сделали бы то же за квадратичное время: каждый элемент первого списка против каждого элемента второго. Пересечение множеств работает за размер меньшего множества.

Задача 3: первый неповторяющийся элемент

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

решение: счётчик и поиск одиночки
sequence = [4, 7, 4, 9, 7, 2, 9, 5]

counts = {}
for n in sequence:
    counts[n] = counts.get(n, 0) + 1

singles = set()
for n in counts:
    if counts[n] == 1:
        singles.add(n)

first = None
for n in sequence:
    if n in singles:
        first = n
        break

print('встретились один раз:', sorted(singles))
print('первый одиночка:', first)
Вывод
встретились один раз: [2, 5]
первый одиночка: 2

Три шага: счётчик собрал частоты, множество singles отобрал элементы с частотой один, второй проход по исходной последовательности нашёл первый элемент из этого множества. Важен порядок проходов: одиночек ищем в исходном порядке, а не в порядке множества — у множества его нет. Проверка n in singles мгновенна за счёт O(1) из урока 5, поэтому даже на длинной последовательности решение остаётся линейным.

Задача 4: гости субботы и воскресенья

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

решение: & и ^ и |
saturday = {'Анна', 'Борис', 'Вера', 'Глеб'}
sunday = {'Вера', 'Глеб', 'Дана', 'Егор'}

both = saturday & sunday
one_day = saturday ^ sunday
all_guests = saturday | sunday

print('пришли оба дня:', sorted(both))
print('только один день:', sorted(one_day))
print('всего гостей за выходные:', len(all_guests))
Вывод
пришли оба дня: ['Вера', 'Глеб']
только один день: ['Анна', 'Борис', 'Дана', 'Егор']
всего гостей за выходные: 6

Симметричная разность ^ из урока 8 забрала тех, кто есть ровно в одном множестве: постоянные посетители Вера и Глеб выпали, а одиночки остались. Объединение посчитало всех без повторов — Анна из субботы и Дана из воскресенья не удвоились. Три вопроса, три операции, ни одного цикла.

Вопрос задачиОперацияУрок
Кто есть в обоихa & bПересечение
Кто только у одногоa - bРазность
Кто ровно в одномa ^ bСимметричная разность
Сколько всего разныхlen(a | b)Объединение
Сколько раз встречалсяСловарь-счётчикКурс про словари

Приём: diff двух наборов

Та же арифметика сравнивает две версии набора: что добавилось и что пропало. Разности из урока 8 работают как diff разделов сайта, зависимостей и списков дел — направление вычитания задаёт вопрос:

решение: две разности
old = {'чаты', 'профиль', 'настройки'}
new = {'чаты', 'профиль', 'карта'}

print('появилось:', sorted(new - old))
print('исчезло:', sorted(old - new))
print('не изменилось:', sorted(old & new))
Вывод
появилось: ['карта']
исчезло: ['настройки']
не изменилось: ['профиль', 'чаты']

Три вызова — три части отчёта о изменениях. Обратите внимание на направления: new - old отвечает на «что появилось», old - new — на «что исчезло»; перепутаете операнды — отчёт будет врать в обратную сторону. Пересечение снизу добирает нетронутое.

Три множества сразу

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

пересечение трёх множеств
post1 = {'python', 'код', 'учёба'}
post2 = {'python', 'книги'}
post3 = {'python', 'код', 'книги'}

in_all = post1 & post2 & post3
print('во всех трёх:', sorted(in_all))
in_two = (post1 & post2) | (post2 & post3) | (post1 & post3)
print('хотя бы в двух:', sorted(in_two))
Вывод
во всех трёх: ['python']
хотя бы в двух: ['python', 'книги', 'код']

Вторая строка собрала парные пересечения и объединила их: тег попадает в ответ, если живёт хотя бы в двух постах. Печать через sorted дает стабильный порядок — сначала латинские строки, потом кириллица, по алфавиту внутри каждой группы: так работает сортировка строк по кодам символов.

проверка: индекс по множеству не берётся
unique = {'Вера', 'Глеб'}
try:
    unique[0]
except TypeError as e:
    print('TypeError:', e)
print('первый по алфавиту:', sorted(unique)[0])
Вывод
TypeError: 'set' object is not subscriptable
первый по алфавиту: Вера
проверка: {} это словарь
empty = set()
not_a_set = {}

print(empty == set(), empty == {})
print(type(not_a_set).__name__)
Вывод
True False
dict

Что дальше

Четыре задачи покрыли половину житейских вопросов к данным: кто уникален, кто общий, кто первый одиночка, кто пришёл оба раза. В следующем уроке та же работа с кортежами: координаты, неизменяемые записи, максимум и минимум сразу. А финальный проект соберёт приёмы обеих задач-практик в один отчёт по журналу посетителей.

Множество в этих задачах — не украшение, а ускоритель: каждая операция заменяет вложенный цикл, и код читается как формулировка вопроса. Кто общий — &, кто уникален — set, кто ровно один раз — ^.

Что выведет код?

Сначала предскажи ответ в голове — это главный навык программиста.

a = {1, 2, 3}
b = {2, 3, 4}
print(len(a & b), len(a - b), len(a | b))
log = ['x', 'y', 'x', 'z']
print(len(log), len(set(log)))
print(sorted(set(log)))
s = set()
for w in ['a', 'b', 'a']:
    s.add(w)
print(len(s), sorted(s))
print('a' in s, 'c' in s)
Проверь себя
0 / 7

1. Как посчитать число уникальных имён в журнале log?

2. Какая операция найдёт людей, которые дружат и с Анной, и с Борисом?

3. Зачем в задаче про первый неповторяющийся элемент нужен словарь-счётчик?

4. Почему одиночку ищут вторым проходом по исходной последовательности, а не по множеству singles?

5. Что вернёт saturday ^ sunday?

6. unique[0] у множества падает. Как взять первый элемент в отсортированном порядке?

7. Как создать пустое множество для накопления результата?

Карточки терминов
Запомнено: 0 / 8
Практика

Даны два списка гостей: friday = ['Анна', 'Борис', 'Анна', 'Вера'] и saturday = ['Вера', 'Глеб', 'Анна']. Приведите оба к множествам, напечатайте отсортированный список тех, кто был в оба дня, отсортированный список всех гостей за два дня и число гостей каждого дня.

practice.py
Вопросы и ответы по уроку

Как найти уникальные элементы списка с помощью множества?

set(список) собирает уникальные, len(set(список)) считает их, sorted(set(список)) печатает в стабильном порядке. Множество печатать напрямую нельзя: порядок внутри него не гарантирован.

Урок 16: дубликаты одной строкой

Как найти общие элементы двух списков?

Привести оба к множествам и взять пересечение: set(a) & set(b). Общие элементы двух списков друзей, товаров, дней — классическое применение пересечения из урока 7.

Урок 7: пересечение множеств

Как найти первый неповторяющийся элемент последовательности?

Считать частоты словарём: counts[n] = counts.get(n, 0) + 1. Отобрать множество одиночек — элементов с частотой один, — затем пройти по исходной последовательности и взять первый элемент, который есть в этом множестве. Порядок гарантирует исходная последовательность, мгновенную проверку — множество.

Почему нельзя писать set(log)[0]?

Множество не хранит порядок, поэтому индексов у него нет: TypeError: 'set' object is not subscriptable. Первый элемент в отсортированном порядке — sorted(set(log))[0]; первый в исходном порядке — проходом по исходному списку с проверкой in.

Чем решение с множествами лучше вложенных циклов?

Скоростью и читаемостью: пересечение множеств работает за размер меньшего множества, вложенные циклы — квадратичное время. Код из одного знака & читается как формулировка вопроса, а цикл с флажками — как алгоритм, который ещё надо понять.

Урок 5: in — мгновенная проверка

Понравился урок? Сошлитесь на него

«Множество отвечает на вопрос «кто вообще приходил» за один вызов: set(log) собирает уникальные имена, len(set(log)) считает их.»

Скопируйте готовую ссылку в формате HTML, Markdown или чистый адрес и вставьте в статью на Habr, VC, Telegram-канал или свой блог — так о проекте узнают новые читатели.

TelegramVK

Что дальше

Осталось в разделе: 20 из 20. Прогресс хранится в вашем браузере.

Похожие уроки по темам

Подобраны автоматически по пересечению тем и ключевых слов.