Какие бывают множества: виды, операции, круги Эйлера и set в Python

Какие бывают множества: виды, операции, круги Эйлера и set в Python Полезное

Множество — это совокупность различимых объектов (элементов), рассматриваемая как единое целое; множество определяется только тем, какие элементы в него входят, без учета порядка и повторов. Множества бывают конечными и бесконечными, пустыми и одноэлементными, числовыми (N, Z, Q, R) и нечисловыми, а по отношению друг к другу — подмножествами, равными, пересекающимися и непересекающимися.

Ниже — классификация видов с примерами, пять основных операций, решение задачи кругами Эйлера и то же самое на set в Python (код проверен на Python 3.14.7).

Мини-словарь: четыре слова, которые путают

Термин Что значит Запись
Элемент Отдельный объект внутри множества a ∈ A («a принадлежит A»)
Подмножество Множество, все элементы которого лежат в другом B ⊆ A
Пустое множество Множество без элементов ∅
Универсальное множество Все объекты, о которых идет речь в задаче U

Главное различие — между «принадлежит» и «является подмножеством». Число 2 принадлежит множеству {1, 2, 3}: 2 ∈ {1, 2, 3}. А множество {2} — его подмножество: {2} ⊆ {1, 2, 3}. Похожая ловушка: ∅ — пустое множество, а {∅} — множество из одного элемента, и этот элемент — пустое множество.

Как задают множество

Способов два. Перечисление: A = {1, 3, 5, 7}. Работает только для конечных множеств, которые удобно выписать. Порядок и повторы не важны: {1, 3, 3, 5} и {5, 3, 1} — одно и то же множество.

Характеристическое свойство: условие, которому удовлетворяет каждый элемент и не удовлетворяет ни один объект вне множества. Например, B = {x ∈ Z | 0 < x < 8, x нечетное} — те же {1, 3, 5, 7}. Так задают и бесконечные множества: {x ∈ R | x > 0}.

Какие бывают множества

По числу элементов

Вид Определение Пример
Пустое Нет ни одного элемента Действительные корни x² + 1 = 0
Одноэлементное Ровно один элемент {0}, корни x — 5 = 0
Конечное Элементы можно пересчитать и счет закончится Буквы русского алфавита (33)
Бесконечное счетное Элементы можно занумеровать 1, 2, 3, … без конца N, Z, Q
Бесконечное несчетное Занумеровать нельзя R, отрезок [0; 1] (любой отрезок ненулевой длины)

Частая ошибка из учебных пересказов: «отрезок — пример конечного множества». Это не так. У отрезка [0; 1] конечная длина, но точек в нем бесконечно много, причем несчетно много. Длина отрезка и число его элементов — разные характеристики.

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

Числовые множества

Обозначение Название Что входит Пример элемента
N Натуральные Числа для счета: 1, 2, 3, … 7
Z Целые Натуральные, ноль и противоположные к натуральным -4
Q Рациональные Дроби m/n, где m ∈ Z, n ∈ N -3/8, 0,25
R Действительные Рациональные и иррациональные √2, π
C Комплексные Числа вида a + bi 2 + 3i

Каждое следующее множество включает предыдущее: N ⊂ Z ⊂ Q ⊂ R ⊂ C. Иррациональные числа (√2, π, e) — это R \ Q, отдельной буквы в школьной традиции обычно нет, иногда пишут I.

Про ноль договоренность разная. В российской школе 0 не считают натуральным: N = {1, 2, 3, …}. В стандарте ISO 80000-2 и во многих зарубежных учебниках и курсах информатики N начинается с нуля. Когда это важно, пишут явно: N₀ = {0, 1, 2, …}.

По отношению друг к другу

  • Равные: A = B, если у них одни и те же элементы.
  • Подмножество: B ⊆ A, если каждый элемент B лежит в A. Если при этом B ≠ A, подмножество собственное (B ⊂ A). Само A — несобственное подмножество A; ∅ по этому определению собственное (для непустого A), и Python считает так же: set() < {1} дает True. В части школьных учебников несобственными называют оба крайних случая, ∅ и A, — проверяйте, какое определение принято в задаче.
  • Пересекающиеся: есть хотя бы один общий элемент.
  • Непересекающиеся: общих элементов нет, A ∩ B = ∅.

У множества из n элементов ровно 2ⁿ подмножеств, включая ∅ и само множество: каждый элемент либо берем, либо нет. У {a, b, c} их 2³ = 8.

Операции над множествами

Разберем на паре A = {1, 2, 3, 4}, B = {3, 4, 5} и универсальном множестве U = {1, …, 10}. Сначала руками, потом кодом.

Операция Запись Что получаем Результат
Объединение A ∪ B Элементы хотя бы одного из множеств {1, 2, 3, 4, 5}
Пересечение A ∩ B Общие элементы {3, 4}
Разность A \ B Элементы A, которых нет в B {1, 2}
Симметрическая разность A △ B Элементы ровно одного из множеств {1, 2, 5}
Дополнение Ā = U \ A Все из U, чего нет в A {5, 6, 7, 8, 9, 10}

Разность несимметрична: A \ B = {1, 2}, а B \ A = {5}. Дополнение имеет смысл только при заданном U: без универсального множества вопрос «что не входит в A» не определен.

Круги Эйлера: решаем задачу

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

Задача. В классе 25 учеников, 15 ходят на футбол, 12 — на шахматы, 7 — и туда, и туда. Сколько не ходят никуда?

Алгоритм для двух кругов:

  1. Нарисовать прямоугольник U (весь класс) и два пересекающихся круга F и C.
  2. В пересечение вписать 7.
  3. Только футбол: 15 — 7 = 8. Только шахматы: 12 — 7 = 5.
  4. Хотя бы один кружок: 8 + 7 + 5 = 20.
  5. Вне кругов: 25 — 20 = 5.

Шаги 3-4 сворачиваются в формулу включений-исключений: |F ∪ C| = |F| + |C| — |F ∩ C| = 15 + 12 — 7 = 20. Пересечение вычитаем, потому что эти 7 учеников посчитаны дважды. Формула верна для конечных множеств; для трех множеств добавляется поправка на тройное пересечение.

Множества в Python: тип set

Полный пример, который повторяет таблицу операций выше:

A = {1, 2, 3, 4}
B = {3, 4, 5}

print("A | B =", A | B)   # объединение
print("A & B =", A & B)   # пересечение
print("A - B =", A - B)   # разность
print("B - A =", B - A)
print("A ^ B =", A ^ B)   # симметрическая разность
print(3 in A, 5 in A)     # принадлежность
print({3, 4} <= A, {3, 4} < A, A < A)  # подмножество / собственное
print(A.isdisjoint({7, 8}))

U = set(range(1, 11))     # универсальное множество задачи
print("дополнение A:", U - A)

Вывод:

A | B = {1, 2, 3, 4, 5}
A & B = {3, 4}
A - B = {1, 2}
B - A = {5}
A ^ B = {1, 2, 5}
True False
True True False
True
дополнение A: {5, 6, 7, 8, 9, 10}

Разбор по строкам:

  • |, &, -, ^ — операторы для ∪, ∩, \ и △. У каждого есть метод-аналог: union, intersection, difference, symmetric_difference. Методы принимают любой итерируемый объект (A.union([7, 8])), операторы — только множества.
  • in проверяет принадлежность (∈). В среднем это O(1) за счет хеш-таблицы, у списка — O(n).
  • <= — подмножество (⊆), < — собственное подмножество (⊂), поэтому A < A дает False. Метод issubset соответствует <=.
  • Отдельного «дополнения» в Python нет: его получают разностью с явно заданным U.
  • Порядок вывода не гарантирован. Небольшие целые в CPython обычно печатаются по возрастанию, но это деталь реализации; строки выводятся в разном порядке от запуска к запуску. Нужен порядок — sorted(A).

Проверим задачу про кружки на конкретных учениках:

students = set(range(1, 26))   # 25 учеников
F = set(range(1, 16))          # футбол: 1..15
C = set(range(9, 21))          # шахматы: 9..20
print(len(F), len(C), len(F & C), len(F | C), len(students - (F | C)))

Вывод 15 12 7 20 5 совпадает с расчетом кругами Эйлера.

Три ошибки новичка

Ошибка 1: пустое множество через фигурные скобки.

s = {}
print(type(s))
s.add(1)
<class 'dict'>
Traceback (most recent call last):
  File "<stdin>", line 3, in <module>
AttributeError: 'dict' object has no attribute 'add'

{} — пустой словарь. Исправление: s = set().

Ошибка 2: изменяемый объект как элемент.

bad = {[1, 2], [3]}
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: cannot use 'list' as a set element (unhashable type: 'list')

Элементы set должны быть хешируемыми. Список заменяют кортежем (1, 2), а множество внутри множества — неизменяемым frozenset. Текст ошибки приведен для Python 3.14; в более старых версиях он короче: unhashable type: 'list'.

Ошибка 3: remove для отсутствующего элемента.

s = {1, 2, 3}
s.discard(10)
print(s)
s.remove(10)

discard молча ничего не делает и печатает {1, 2, 3}, а remove падает с KeyError: 10. Выбор: remove, если отсутствие элемента — ошибка в логике программы, discard, если это нормальная ситуация.

Где математика и set расходятся

print({1, True, 1.0})
print(len({1, True, 1.0}))
evens = {x for x in range(20) if x % 2 == 0}
print(sorted(evens))

Вывод: {1}, 1, [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]. Python считает 1, True и 1.0 равными (у них равные хеши и 1 == True), поэтому в множестве остается один элемент. Для математика это три разных объекта.

Вторая граница: set всегда конечен и хранится в памяти. N или R в виде set не записать. Бесконечное множество в коде задают характеристическим свойством — функцией-предикатом lambda x: x > 0 — или генератором, а в set собирают только конечную выборку, как evens выше.

В других языках идея та же, меняются названия и детали: в C# это HashSet<T> и SortedSet<T>, подробно — в статье про множества в C#.

Выводы

  • Множество определяется только составом элементов: порядок и повторы не важны.
  • Виды по числу элементов: пустое, одноэлементное, конечное, бесконечное (счетное как N, Z, Q или несчетное как R); отрезок — бесконечное множество.
  • Числовые множества вложены: N ⊂ Z ⊂ Q ⊂ R ⊂ C; включать ли 0 в N, зависит от договоренности.
  • Пять операций — объединение, пересечение, разность, симметрическая разность, дополнение (только относительно U); задачи на подсчет решаются кругами Эйлера и формулой |A ∪ B| = |A| + |B| — |A ∩ B|.
  • В Python set повторяет эти операции операторами | & - ^ <= <, но хранит только хешируемые элементы, конечен и считает 1, True и 1.0 одним элементом.

Где применяется / связь с практикой

Освойте тему на практике

На множествах построены реляционные базы (UNION, INTERSECT, EXCEPT в SQL), удаление дубликатов, проверка прав доступа, теория вероятностей (события — подмножества пространства исходов) и графы. Если хочется системно подтянуть математику для анализа данных и программирования — от множеств и логики до вероятностей и линейной алгебры, — посмотрите курс «Базовая математика для цифровых профессий». Разобрать отдельные темы с преподавателем вживую можно на открытых уроках Otus.

FAQ

Пустое множество — подмножество любого множества?
Да. Для ∅ ⊆ A нужно, чтобы каждый элемент ∅ лежал в A, а элементов у ∅ нет, так что условие выполняется для любого A.

Чем множество отличается от массива или списка?
В списке важен порядок и допустимы повторы, во множестве — нет. Поэтому [1, 2, 2] и [2, 1] разные списки, а {1, 2, 2} и {2, 1} одно множество.

Как сделать множество, которое можно положить в другое множество или использовать как ключ словаря?
Использовать frozenset: он неизменяемый и хешируемый, например {frozenset({1}), frozenset({1, 2})} работает.

OTUS Журнал
Скидка 5% 12-19 октября на курсы (popup)
Бесплатные открытые уроки (поп-ап)