JavaRush /Курсы /Модуль 1: Python Core /Применение ДП в реальных задачах

Применение ДП в реальных задачах

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

7.1 Оптимизация динамических алгоритмов

Оптимизация динамических алгоритмов направлена на улучшение их временной и пространственной эффективности. Существует несколько подходов к оптимизации, включая использование мемоизации, сокращение используемой памяти и оптимизацию рекурсии.

1. Мемоизация:

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

Пример:

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


def fibonacci(n, memo={}):
    if n in memo:
        return memo[n]
    if n <= 2:
        return 1
    memo[n] = fibonacci(n - 1, memo) + fibonacci(n - 2, memo)
    return memo[n]
        
        

2. Табличное решение (Bottom-Up):

Табличное решение (bottom-up) строит таблицу решений для всех возможных подзадач от базового случая до целевой задачи. Это позволяет избежать накладных расходов на рекурсивные вызовы.

Пример:

В задаче о рюкзаке построение таблицы минимальных количеств монет для каждой суммы от 0 до S.


def fibonacci(n):
    dp = [0] * (n + 1)
    dp[1] = dp[2] = 1
    for i in range(3, n + 1):
        dp[i] = dp[i - 1] + dp[i - 2]
    return dp[n]
        
        

3. Сокращение используемой памяти:

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

Пример:

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


def knapsack_optimized(weights, values, W):
    n = len(weights)
    dp = [0] * (W + 1)
    for i in range(n):
        for w in range(W, weights[i] - 1, -1):
            dp[w] = max(dp[w], dp[w - weights[i]] + values[i])
    return dp[W]
        
        

4. Хвостовая рекурсия:

Хвостовая рекурсия — это рекурсивный вызов, который выполняется в конце функции. Это позволяет компилятору или интерпретатору оптимизировать стек вызовов.

Пример:

В задаче вычисления чисел Фибоначчи можно использовать хвостовую рекурсию с накопителем результата.

7.2 Применение динамического программирования в реальных задачах

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

1. Оптимизация маршрутов и логистика:

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

Пример:

Задача коммивояжера (Travelling Salesman Problem, TSP) — найти кратчайший путь, проходящий через все города.


def tsp(graph, start):
    n = len(graph)
    dp = [[None] * (1 << n) for _ in range(n)]

    def visit(city, visited):
        if visited == (1 << n) - 1:
            return graph[city][start]
        if dp[city][visited] is not None:
            return dp[city][visited]
        result = float('inf')
        for next_city in range(n):
            if visited & (1 << next_city) == 0:
                result = min(result, graph[city][next_city] + visit(next_city, visited | (1 << next_city)))
        dp[city][visited] = result
        return result

    return visit(start, 1 << start)

2. Выравнивание последовательностей в биоинформатике:

В биоинформатике динамическое программирование используется для выравнивания ДНК, РНК и белковых последовательностей.

Пример:

Алгоритм Нидлмана-Вунша (Needleman-Wunsch) для глобального выравнивания последовательностей и алгоритм Смита-Ватермана (Smith-Waterman) для локального выравнивания.


def lcs(X, Y):
    m, n = len(X), len(Y)
    dp = [[0] * (n + 1) for _ in range(m + 1)]
    for i in range(1, m + 1):
        for j in range(1, n + 1):
            if X[i - 1] == Y[j - 1]:
                dp[i][j] = dp[i - 1][j - 1] + 1
            else:
                dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])
    return dp[m][n]
        

3. Финансовые расчеты и экономическое планирование:

Динамическое программирование применяется для оптимизации инвестиционных портфелей, управления рисками и планирования производства.

Пример:

Задача о размене монет и задача о рюкзаке используются для управления активами и оптимального распределения ресурсов.

4. Управление запасами и производством:

В производстве и управлении запасами динамическое программирование помогает оптимизировать процессы и минимизировать затраты.

Пример:

Модель управления запасами (Inventory Management Model) для минимизации издержек на хранение и заказ продукции.

5. Машинное обучение и искусственный интеллект:

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

Пример:

Алгоритмы обучения на основе динамического программирования, такие как метод обратного распространения в нейронных сетях.

2
Задача
Модуль 1: Python Core, 19 уровень, 6 лекция
Недоступна
Возрастающая подпоследовательность.
Возрастающая подпоследовательность.
2
Задача
Модуль 1: Python Core, 19 уровень, 6 лекция
Недоступна
Путь в графе
Путь в графе
Комментарии (1)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Slevin Уровень 64
20 августа 2025
4. Хвостовая рекурсия: Хвостовая рекурсия — это рекурсивный вызов, который выполняется в конце функции. Это позволяет компилятору или интерпретатору оптимизировать стек вызовов. В Python нет механизма, который автоматически преобразует хвостовую рекурсию в цикл. Каждый рекурсивный вызов создаёт новый фрейм стека, даже если вызов находится в «хвосте». Даже если переписать рекурсивную функцию в хвостовую форму, глубина рекурсии всё равно ограничена sys.getrecursionlimit() (~1000 по умолчанию). Для больших значений рекурсия вызовет RecursionError. --- (1 << n) А это 2^n, объяснять это конечно же не нужно, как и сам алгоритм Беллмана–Хелда–Карпа. И кстати, советую даже не пытаться разбирать этот код, да и сам алгоритм, ибо я вот, например, попытался 😥 Алгоритм Нидлмана-Вунша (Needleman-Wunsch) для глобального выравнивания последовательностей и алгоритм Смита-Ватермана (Smith-Waterman) для локального выравниванияю. Алгоритм для чего? Выравнивания кого и куда?