hocvietcode.com
  • Trang chủ
  • Học lập trình
    • Lập trình C/C++
    • Lập trình HTML
    • Lập trình Javascript
      • Javascript cơ bản
      • ReactJS framework
      • AngularJS framework
      • Typescript cơ bản
      • Angular
    • Lập trình Mobile
      • Lập Trình Dart Cơ Bản
        • Dart Flutter Framework
    • Cơ sở dữ liệu
      • MySQL – MariaDB
      • Micrsoft SQL Server
      • Extensible Markup Language (XML)
      • JSON
    • Lập trình PHP
      • Lập trình PHP cơ bản
      • Laravel Framework
    • Lập trình Java
      • Java Cơ bản
    • Cấu trúc dữ liệu và giải thuật
    • Lập Trình C# Cơ Bản
    • Machine Learning
  • WORDPRESS
    • WordPress cơ bản
    • WordPress nâng cao
    • Chia sẻ WordPress
  • Kiến thức hệ thống
    • Microsoft Azure
    • Docker
    • Linux
  • Chia sẻ IT
    • Tin học văn phòng
      • Microsoft Word
      • Microsoft Excel
    • Marketing
      • Google Adwords
      • Facebook Ads
      • Kiến thức khác
    • Chia sẻ phần mềm
    • Review công nghệ
    • Công cụ – tiện ích
      • Kiểm tra bàn phím online
      • Kiểm tra webcam online
Đăng nhập
  • Đăng nhập / Đăng ký

Please enter key search to display results.

Home
  • Cấu trúc dữ liệu và giải thuật
Giải thuật Qui hoạch động (Dynamic Programming) 

Giải thuật Qui hoạch động (Dynamic Programming) 

  • 30-09-2024
  • Toanngo92
  • 0 Comments

Qui hoạch động (Dynamic Programming – DP) là một kỹ thuật thiết kế thuật toán rất mạnh, được sử dụng để giải quyết các bài toán tối ưu hóa bằng cách chia chúng thành các bài toán con chồng lấn, sau đó giải quyết từng bài toán con một lần và lưu trữ kết quả để sử dụng lại khi cần thiết.

Mục lục

  • Ý tưởng chính của qui hoạch động:
  • Các bước để giải quyết bài toán bằng qui hoạch động:
  • Ví dụ bài toán sử dụng qui hoạch động:
    • 1. Bài toán Fibonacci
    • Công thức đệ quy Fibonacci:
    • Cách giải bằng qui hoạch động:
    • Giải thích:
    • Độ phức tạp:
    • 2. Bài toán balô (Knapsack Problem)
    • Cách giải bài toán balô 0-1 bằng qui hoạch động:
    • Giải thích:
    • Độ phức tạp:
  • Các vấn đề qui hoạch động thường gặp:
  • Phân loại qui hoạch động:
  • Ưu điểm của qui hoạch động:

Ý tưởng chính của qui hoạch động:

  1. Chia bài toán thành các bài toán con nhỏ hơn: Bài toán gốc được chia thành các bài toán con đơn giản hơn.
  2. Ghi nhớ lời giải của các bài toán con (Memoization): Kết quả của mỗi bài toán con được lưu trữ (thường sử dụng mảng hoặc bảng) để tránh việc phải tính lại những bài toán con giống nhau trong tương lai.
  3. Sử dụng lại lời giải: Khi gặp lại bài toán con đã giải, thuật toán sẽ sử dụng lại kết quả đã lưu thay vì tính lại, giúp tăng hiệu suất.

Điểm khác biệt quan trọng giữa qui hoạch động và phương pháp chia để trị là qui hoạch động lưu trữ kết quả của các bài toán con để tránh tính toán lặp lại, trong khi chia để trị không lưu trữ kết quả, do đó có thể dẫn đến việc giải lại nhiều lần cùng một bài toán con.

Các bước để giải quyết bài toán bằng qui hoạch động:

  1. Xác định trạng thái (state): Tìm cách biểu diễn bài toán dưới dạng các trạng thái.
  2. Xác định công thức truy hồi (recurrence relation): Thiết lập mối liên hệ giữa các trạng thái để có thể tính toán trạng thái lớn hơn từ các trạng thái nhỏ hơn.
  3. Xác định các trường hợp cơ sở (base cases): Xác định giá trị khởi đầu của bài toán.
  4. Tính toán và lưu trữ kết quả: Áp dụng công thức truy hồi và lưu trữ kết quả vào bảng (table) để tránh tính toán lại.

Ví dụ bài toán sử dụng qui hoạch động:

1. Bài toán Fibonacci

Bài toán tính số Fibonacci thứ n là một ví dụ kinh điển để minh họa qui hoạch động.

Công thức đệ quy Fibonacci:

  • F(n) = F(n-1) + F(n-2)
  • Với điều kiện cơ sở:
  • F(0) = 0
  • F(1) = 1

Cách giải bằng qui hoạch động:

public class FibonacciDP {

    // Hàm tính Fibonacci bằng qui hoạch động
    public static int fibonacci(int n) {
        // Tạo một mảng để lưu kết quả của các số Fibonacci
        int[] fib = new int[n + 1];

        // Điều kiện cơ sở
        fib[0] = 0;
        fib[1] = 1;

        // Tính các giá trị Fibonacci từ 2 đến n
        for (int i = 2; i <= n; i++) {
            fib[i] = fib[i - 1] + fib[i - 2];
        }

        // Trả về Fibonacci của n
        return fib[n];
    }

    public static void main(String[] args) {
        int n = 10;
        System.out.println("Fibonacci of " + n + " is " + fibonacci(n));
    }
}

Giải thích:

  • Xác định trạng thái: Ở đây, trạng thái là F(n) – giá trị của số Fibonacci thứ n.
  • Công thức truy hồi: F(n) = F(n-1) + F(n-2).
  • Điều kiện cơ sở: F(0) = 0 và F(1) = 1.
  • Tính toán và lưu trữ: Sử dụng mảng fib[] để lưu trữ kết quả của các số Fibonacci đã tính được và sử dụng lại khi cần.

Độ phức tạp:

  • Thời gian: O(n) vì chúng ta chỉ tính mỗi số Fibonacci một lần.
  • Không gian: O(n) vì cần lưu trữ kết quả của tất cả các số Fibonacci từ 0 đến n.

2. Bài toán balô (Knapsack Problem)

Bài toán balô 0-1 là một ví dụ phổ biến khác của qui hoạch động. Cho một balô có sức chứa nhất định và một danh sách các vật phẩm, mỗi vật phẩm có một giá trị và một trọng lượng, nhiệm vụ là tìm cách chọn một số vật phẩm sao cho tổng giá trị là lớn nhất mà không vượt quá sức chứa của balô.

Cách giải bài toán balô 0-1 bằng qui hoạch động:

public class KnapsackDP {

    // Hàm tính giá trị lớn nhất có thể đưa vào balô
    public static int knapsack(int W, int[] weights, int[] values, int n) {
        int[][] K = new int[n + 1][W + 1];

        // Duyệt qua tất cả các vật phẩm
        for (int i = 0; i <= n; i++) {
            for (int w = 0; w <= W; w++) {
                if (i == 0 || w == 0) {
                    K[i][w] = 0;
                } else if (weights[i - 1] <= w) {
                    K[i][w] = Math.max(values[i - 1] + K[i - 1][w - weights[i - 1]], K[i - 1][w]);
                } else {
                    K[i][w] = K[i - 1][w];
                }
            }
        }

        return K[n][W];
    }

    public static void main(String[] args) {
        int[] values = {60, 100, 120};
        int[] weights = {10, 20, 30};
        int W = 50;
        int n = values.length;

        System.out.println("Maximum value in Knapsack = " + knapsack(W, weights, values, n));
    }
}

Giải thích:

  • Xác định trạng thái: Trạng thái ở đây là K[i][w] – giá trị tối đa có thể đạt được với i vật phẩm và sức chứa balô w.
  • Công thức truy hồi: Nếu vật phẩm thứ i có thể cho vào balô, giá trị tối đa là:
    K[i][w] = max(values[i-1] + K[i-1][w-weights[i-1]], K[i-1][w])
  • Điều kiện cơ sở: Khi không có vật phẩm nào hoặc balô có sức chứa bằng 0, giá trị tối đa là 0.
  • Tính toán và lưu trữ: Kết quả của mỗi trạng thái được lưu trong bảng K.

Độ phức tạp:

  • Thời gian: O(n * W), trong đó n là số lượng vật phẩm và W là sức chứa của balô.
  • Không gian: O(n * W), vì cần bảng K để lưu trữ kết quả cho từng cặp i và w.

Các vấn đề qui hoạch động thường gặp:

  • Bài toán balô (Knapsack problem).
  • Bài toán đường đi ngắn nhất (Shortest Path): Floyd-Warshall, Bellman-Ford.
  • Bài toán xâu con chung dài nhất (Longest Common Subsequence – LCS).
  • Bài toán phân tích chuỗi (String Parsing): Sử dụng DP để tìm lời giải tối ưu cho các vấn đề liên quan đến xử lý chuỗi, ví dụ như kiểm tra tính đối xứng (Palindrome), chỉnh sửa chuỗi (Edit Distance).

Phân loại qui hoạch động:

  1. Top-down (Memoization): Sử dụng đệ quy, bắt đầu từ bài toán lớn, chia nhỏ dần và lưu lại kết quả của từng bài toán con trong quá trình quay lui. Khi gặp lại bài toán con đã giải, chỉ cần sử dụng kết quả đã lưu.
  2. Bottom-up (Tabulation): Bắt đầu giải từ các bài toán con nhỏ nhất, lưu kết quả trong bảng và dùng các kết quả này để tính các bài toán lớn hơn.

Ưu điểm của qui hoạch động:

  • Giảm chi phí tính toán: Giải quyết hiệu quả các bài toán có nhiều bài toán con trùng lặp.
  • Hiệu suất cao: Giảm thời gian chạy từ đệ quy đơn giản (có thể là O(2^n)) xuống còn O(n) hoặc O(n^2) tùy thuộc vào bài toán.

Qui hoạch động là một phương pháp mạnh mẽ để giải quyết các bài toán tối ưu hóa bằng cách giải quyết các bài toán con chồng lấn một cách hiệu quả. Phương pháp này đặc biệt hữu ích trong các bài toán phức tạp với nhiều bài toán con cần tính toán lặp lại, giúp tối ưu hóa thời gian chạy và không gian bộ nhớ.

Bài viết liên quan:

Cấu trúc dữ liệu danh sách liên kết (LinkedList)
Cấu trúc dữ liệu Mảng (Array)
Giới thiệu về thuật toán
Định lý thợ (Master Theorem)
Giải thuật chia để trị (Divide and Conquer)
Phân tích tiệm cận trong CTDL và giải thuật
Giải thuật tham lam (Greedy Algorithm)
Đệ quy trong lập trình
Thuật toán tìm kiếm nhảy – Jump Search
Thuật toán tìm kiếm nhị phân – binary search
Thuật toán tìm kiếm tuần tự – linear search
Sắp xếp trộn – Merge Sort

THÊM BÌNH LUẬN Cancel reply

Dịch vụ thiết kế Wesbite

NỘI DUNG MỚI CẬP NHẬT

2. PHÂN TÍCH VÀ ĐẶC TẢ HỆ THỐNG

1. TỔNG QUAN KIẾN THỨC THỰC HÀNH TRIỂN KHAI DỰ ÁN CÔNG NGHỆ THÔNG TIN

Hướng dẫn tự cài đặt n8n comunity trên CyberPanel, trỏ tên miền

Mẫu prompt tạo mô tả chi tiết bối cảnh

Một số cải tiến trong ASP.NET Core, Razor Page, Model Binding, Gabbage collection

Giới thiệu

hocvietcode.com là website chia sẻ và cập nhật tin tức công nghệ, chia sẻ kiến thức, kỹ năng. Chúng tôi rất cảm ơn và mong muốn nhận được nhiều phản hồi để có thể phục vụ quý bạn đọc tốt hơn !

Liên hệ quảng cáo: [email protected]

Kết nối với HỌC VIẾT CODE

© hocvietcode.com - Tech888 Co .Ltd since 2019

Đăng nhập

Trở thành một phần của cộng đồng của chúng tôi!
Registration complete. Please check your email.
Đăng nhập bằng google
Đăng kýBạn quên mật khẩu?

Create an account

Welcome! Register for an account
The user name or email address is not correct.
Registration confirmation will be emailed to you.
Log in Lost your password?

Reset password

Recover your password
Password reset email has been sent.
The email could not be sent. Possible reason: your host may have disabled the mail function.
A password will be e-mailed to you.
Log in Register
×