CodeGym /Kursy /Python SELF PL /Podstawy programowania dynamicznego

Podstawy programowania dynamicznego

Python SELF PL
Poziom 60 , Lekcja 0
Dostępny

5.1 Definicja programowania dynamicznego.

Programowanie dynamiczne (Dynamic Programming, DP) — to metoda optymalizacji, która jest używana do rozwiązywania skomplikowanych problemów poprzez rozbijanie ich na prostsze podzadania. Programowanie dynamiczne jest efektywne dla zadań, które można podzielić na nakładające się podzadania z optymalną strukturą.

Zasady działania:

1. Optymalna podstruktura:

Zadanie posiada optymalną podstrukturę, jeśli jego optymalne rozwiązanie można zbudować z optymalnych rozwiązań jego podzadań. Oznacza to, że można rozwiązać duży problem, rozwiązując kilka mniejszych podzadań.

2. Nakładające się podzadania:

Zadanie ma nakładające się podzadania, jeśli jego podzadania powtarzają się kilka razy podczas rozwiązywania. Programowanie dynamiczne efektywnie rozwiązuje zadania z nakładającymi się podzadaniami poprzez zapamiętywanie wyników już rozwiązanych podzadań (memoizacja).

3. Memoizacja i tabulacja:

Memoizacja (top-down): Rekurencyjne podejście, w którym wyniki podzadań są przechowywane w pamięci, aby uniknąć ponownych obliczeń.

Tabulacja (bottom-up): Iteracyjne podejście, w którym wyniki podzadań są obliczane i przechowywane w tabeli (zwykle w tablicy) i używane do obliczenia ostatecznego wyniku.

Przykłady zadań rozwiązywanych metodą programowania dynamicznego

5.2 Problem plecakowy.

Zadanie:

Mamy zbiór przedmiotów, każdy z wagą i wartością. Trzeba wybrać przedmioty do plecaka o ograniczonej pojemności, aby zmaksymalizować całkowitą wartość.

Ważne! W odróżnieniu od podobnego zadania, które można dobrze rozwiązać algorytmem zachłannym, w tym zadaniu nie można dzielić przedmiotów.

Rozwiązanie:

Tworzymy tabelę, gdzie wiersze odpowiadają przedmiotom, a kolumny — możliwej pojemności plecaka. Wartość w komórce reprezentuje maksymalną wartość dla danej liczby przedmiotów i pojemności.

Złożoność czasowa: O(n * W), gdzie n — liczba przedmiotów, W — pojemność plecaka.

Przykład kodu w Python:


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

5.3 Problem najkrótszej ścieżki

Zadanie:

Znajdź najkrótsze ścieżki między wszystkimi parami wierzchołków w ważonym grafie.

Rozwiązanie:

Używana jest tabela, gdzie wiersze i kolumny odpowiadają wierzchołkom grafu. Wartość w komórce reprezentuje najkrótszą odległość między dwoma wierzchołkami.

Złożoność czasowa: O(V^3), gdzie V — liczba wierzchołków

Przykład kodu w Python:


def floyd_warshall(graph):
    v = len(graph)
    dist = list(map(lambda i: list(map(lambda j: j, i)), graph))
            
    for k in range(v):
        for i in range(v):
            for j in range(v):
                dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j])
            
    return dist
        
        

5.4 Porównanie programowania dynamicznego z innymi metodami.

Porównanie z metodą brute force:

Złożoność czasowa:

  • Brute force: często ma wykładniczą złożoność czasową (na przykład, O(2^n) dla problemu Fibonacciego).
  • Programowanie dynamiczne: zmniejsza złożoność czasową do wielomianowej (na przykład, O(n) dla problemu Fibonacciego).

Złożoność przestrzenna:

  • Brute force: może używać mniej pamięci, ale koszty czasowe są wysokie.
  • Programowanie dynamiczne: może używać więcej pamięci do przechowywania wyników pośrednich (na przykład, O(n) dla problemu Fibonacciego z memoizacją), ale wygrywa na czasie.

Porównanie z algorytmami zachłannymi (greedy algorithms):

Optymalność:

  • Algorytmy zachłanne: nie zawsze znajdują globalnie optymalne rozwiązanie, ale działają szybciej i są prostsze do wdrożenia.
  • Programowanie dynamiczne: znajduje globalnie optymalne rozwiązanie, ale wymaga więcej zasobów obliczeniowych.

Przykłady zadań:

  • Algorytmy zachłanne: problem plecakowy z ułamkami (fractional knapsack), minimalne drzewo rozpinające (MST).
  • Programowanie dynamiczne: problem plecakowy (całkowitoliczbowy), najdłuższy wspólny podciąg (LCS).
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION