Задачи на множества Python: практика с решениями
Четыре задачи на множества из жизни данных: уникальные посетители журнала, общие друзья, первый неповторяющийся элемент и гости, пришедшие оба дня. Код, вывод и разбор каждого шага.
Редакция Питоники
Восемнадцать уроков строили инструментарий: уникальность из урока 1, мгновенный in из урока 5, пересечение и разность из уроков 7 и 8. Пора поработать этим инструментом по назначению. Четыре задачи ниже — из практики данных: журнал визитов, круги общения, поиск первого одиночки, сравнение двух дней. Каждая решается множествами за несколько строк, и в каждой разбор объясняет, почему именно эта операция, а не цикл с флажками.
Задача 1: уникальные посетители журнала
Журнал содержит запись на каждый визит, поэтому имена повторяются. Требуется ответить на три вопроса: сколько было визитов, сколько разных гостей и кто именно. Множество отвечает на вопрос «кто вообще приходил» за один вызов: set(log) собирает уникальные имена, len(set(log)) считает их.
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)
1. Как посчитать число уникальных имён в журнале log?
2. Какая операция найдёт людей, которые дружат и с Анной, и с Борисом?
3. Зачем в задаче про первый неповторяющийся элемент нужен словарь-счётчик?
4. Почему одиночку ищут вторым проходом по исходной последовательности, а не по множеству singles?
5. Что вернёт saturday ^ sunday?
6. unique[0] у множества падает. Как взять первый элемент в отсортированном порядке?
7. Как создать пустое множество для накопления результата?
Даны два списка гостей: friday = ['Анна', 'Борис', 'Анна', 'Вера'] и saturday = ['Вера', 'Глеб', 'Анна']. Приведите оба к множествам, напечатайте отсортированный список тех, кто был в оба дня, отсортированный список всех гостей за два дня и число гостей каждого дня.
Как найти уникальные элементы списка с помощью множества?
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-канал или свой блог — так о проекте узнают новые читатели.
Что дальше
Осталось в разделе: 20 из 20. Прогресс хранится в вашем браузере.
Что читать дальше
Множества · Урок 7
Пересечение множеств Python: оператор & и intersection
Общие элементы двух множеств одним знаком: & находит тех, кто есть в обоих. Пустое пересечение, isdisjoint и пересечение трёх наборов.
Множества · Урок 8
Разность и симметричная разность множеств в Python
Разность отвечает на вопрос «что есть у первого и чего нет у второго», симметричная — «что есть ровно у одного из двух». Операторы - и ^, методы difference и symmetric_difference и почему порядок множеств важен.
Множества · Урок 20
Проект: анализ уникальных посетителей на Python
Итоговый проект: журнал из 12 визитов вида «имя:день» превращается в отчёт — уникальные посетители, гости по дням, постоянные через пересечение, топ повторов. Три куска, каждый самодостаточен.
Похожие уроки по темам
Подобраны автоматически по пересечению тем и ключевых слов.
Циклы · Урок 1
Цикл for в Python: первый цикл
Первый цикл for: компьютер повторяет одну строку за вас. Перебираем список, разбираем переменную цикла и тело с отступами — и видим, зачем цикл вместо копипасты.
for python для начинающихперебор списка python
NumPy · Урок 1
Что такое NumPy и как установить через pip: первый массив ndarray
Первый массив ndarray: создаём, сравниваем со списком, разбираем dtype и shape — и ускоряем сумму миллиона чисел примерно в сто раз.
numpy для начинающихчем ndarray отличается от списка python
Множества · Урок 1
Set в Python: первое множество и исчезающие дубликаты
Список терпит дубликаты, множество — нет: {1, 3, 3} хранит каждый элемент один раз. Первое множество, исчезающие повторы и честная печать через sorted.
множества pythonуникальные элементы python