JavaRush /Курсы /Модуль 1: Python Core /Комбинаторные алгоритмы

Комбинаторные алгоритмы

Модуль 1: Python Core
19 уровень , 7 лекция
Открыта

8.1 Определение комбинаторных алгоритмов

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

Основные типы комбинаторных задач

  • Перестановки: Все возможные упорядоченные комбинации элементов множества.
  • Сочетания: Все возможные неупорядоченные подмножества заданного размера.
  • Размещения: Все возможные упорядоченные подмножества заданного размера.
  • Разбиения: Все возможные способы разбить множество на подмножества.
  • Комбинаторные объекты: Другие структуры, такие как графы, матрицы и т.д.

Преимущества и недостатки комбинаторных алгоритмов

Преимущества:

  • Полное перечисление: Эти алгоритмы могут перечислять все возможные комбинации, что полезно для задач исчерпывающего поиска.
  • Гибкость: Их можно адаптировать для решения широкого спектра задач, связанных с комбинаторикой.
  • Интуитивность: Многим алгоритмы легко понять и реализовать благодаря рекурсивному подходу и технике обратного отслеживания (backtracking).

Недостатки:

  • Комбинаторный взрыв: С ростом размера входных данных время выполнения и объем памяти могут резко увеличиваться (например, O(n!)).
  • Неоптимальность: В случае большого числа комбинаций эти алгоритмы могут быть неэффективными и требовать улучшения или замены на более продвинутые техники (например, использование динамического программирования или жадных алгоритмов).

Временная и пространственная сложность комбинаторных алгоритмов

Временная сложность:

  • Перестановки: O(n!), где n — количество элементов множества.
  • Сочетания: O(C(n, k)), где C(n, k) — биномиальный коэффициент, равный n! / (k! * (n - k)!).
  • Размещения: O(A(n, k)), где A(n, k) — количество размещений, равное n! / (n - k)!.
  • Подмножества: O(2^n), так как каждое из n элементов может быть включено или не включено в подмножество.

Пространственная сложность:

Обычно O(n) для хранения промежуточных результатов в процессе рекурсивного построения, но итоговая память может потребоваться для хранения всех результатов (например, O(n * n!) для перестановок).

Примеры комбинаторных алгоритмов

8.2 Генерация перестановок

Задача:

Найти все уникальные перестановки элементов заданного множества.

Алгоритм:

Используется рекурсия или итеративные методы для генерации всех возможных упорядоченных комбинаций элементов.


def permute(nums):
    result = []

    def backtrack(start):
        if start == len(nums):
            result.append(nums[:])
        for i in range(start, len(nums)):
            nums[start], nums[i] = nums[i], nums[start]
            backtrack(start + 1)
            nums[start], nums[i] = nums[i], nums[start]

    backtrack(0)
    return result

print(permute([1, 2, 3]))

8.3 Генерация сочетаний

Задача:

Найти все сочетания заданного размера из множества.

Алгоритм:

Используется рекурсия или итерация для генерации всех возможных подмножеств заданного размера.


def combine(n, k):
    result = []

    def backtrack(start, path):
        if len(path) == k:
            result.append(path[:])
            return
        for i in range(start, n + 1):
            path.append(i)
            backtrack(i + 1, path)
            path.pop()

    backtrack(1, [])
    return result

print(combine(4, 2))
        

8.4 Генерация разбиений множества

Задача:

Найти все возможные способы разбить множество на подмножества.

Алгоритм:

Используется рекурсия для генерации всех возможных разбиений множества.


import copy

def partition_set(s):
    result = []

    def backtrack(part, rest):
        if not rest:
            result.append(copy.deepcopy(part[:]))
            return
        for i in range(len(part)):
            part[i].append(rest[0])
            backtrack(part, rest[1:])
            part[i].pop()
        part.append([rest[0]])
        backtrack(part, rest[1:])
        part.pop()

    backtrack([], list(s))
    return result

print(partition_set({1, 2, 3}))
        
        

Использование copy.deepcopy(part[:]) создает глубокую копию списка part. Это означает, что создается новый, независимый список, который не связан с исходным part. Таким образом, каждый элемент в result будет представлять собой уникальное разбиение.

2
Задача
Модуль 1: Python Core, 19 уровень, 7 лекция
Недоступна
Перестановки множества
Перестановки множества
2
Задача
Модуль 1: Python Core, 19 уровень, 7 лекция
Недоступна
Все подмножества
Все подмножества
Комментарии (1)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Slevin Уровень 64
21 августа 2025
Подсказка тем, кто будет здесь после меня: Не пытайтесь придумать алгоритм (для второй задачи) сами, это не ваша работа и не ваша задача - нагуглите текстовый алгоритм поиска подмножеств - и напишите код на основании его шагов. Этого более чем достаточно для программиста.