Phương pháp này đã được ápdụng để giải hàng loạt bài toán thực tế trong các quá trình kỹ thuật cộng nghệ, tổchức sản xuất, kế hoạch hoá kinh tế… Trong thực tế, ta thường gặp một số bài t
Trang 1Chuyên đề PHƯƠNG PHÁP QUY HOẠCH ĐỘNG
1 Nguyên lý tối ưu của Bellman
Phương pháp quy hoạch động cùng nguyên lý tối ưu được nhà toán học MỹR.Bellman đề xuất vào những năm 50 của thế kỷ 20 Phương pháp này đã được ápdụng để giải hàng loạt bài toán thực tế trong các quá trình kỹ thuật cộng nghệ, tổchức sản xuất, kế hoạch hoá kinh tế…
Trong thực tế, ta thường gặp một số bài toán tối ưu loại sau: Có một đại lượng
f hình thành trong một quá trình gồm nhiều giai đoạn và ta chỉ quan tâm đến kết quả cuối cùng là giá trị của f phải lớn nhất hoặc nhỏ nhất, ta gọi chung là giá trị tối ưu của
f Giá trị của f phụ thuộc vào những đại lượng xuất hiện trong bài toán mà mỗi bộ giá trị của chúng được gọi là một trạng thái của hệ thống và phụ thuộc vào cách thức đạt được giá trị f trong từng giai đoạn mà mỗi cách tổ chức được gọi là một điều khiển Đại lượng f thường được gọi là hàm mục tiêu và quá trình đạt được giá trị tối ưu của f được gọi là quá trình điều khiển tối ưu.
Bellman phát biểu nguyên lý tối ưu (cũng gọi là nguyên lý Bellman) mà ý
tưởng cơ bản là như sau: “Với mỗi quá trình điều khiển tối ưu, đối với trạng thái bắt
đầu A0, với trạng thái A trong quá trình đó, phần quá trình kể từ trạng thái A xem như
trạng thái bắt đầu cũng là tối ưu”
Chú ý rằng nguyên lý này được thừa nhận mà không chứng minh.
Phương pháp tìm điều khiển tối ưu theo nguyên lý Bellman thường được gọi là
quy hoạch động Thuật ngữ này nói lên thực chất của quá trình điều khiển là động:
có thể trong một số bước đầu tiên lựa chọn điều khiển tối ưu dường như không tốtnhưng tựu chung cả quá trình lại là tốt nhất
Hiểu một cách đơn giản hơn quy hoạch động là phương pháp giải bài toán từ
nhỏ đến lớn, việc giải – tìm phương án tối ưu của các bài toán nhỏ và lưu trữ các kếtquả này lại sẽ giúp ta có thể giải các bài toán với kích thước lớn dần đến khi đạt đượckết quả mong muốn
Trang 22 Ý tưởng và nội dung của phương pháp quy hoạch động
Xét bài toán sau:
Cho một dãy N số nguyên A1, A2,…,AN Hãy tìm cách xoá đi một số ít nhất số
hạng để dãy còn lại là đơn điệu hay nói cách khác hãy chọn một số nhiều nhất các số
hạng sao cho dãy B gồm các số hạng đó theo trình tự xuất hiện trong dãy A là đơn
điệu
Quá trình chọn B được điều khiển qua N giai đoạn để đạt được mục tiêu là số lượng số hạng của dãy B là nhiều nhất, điều khiển ở giai đoạn i thể hiện việc chọn hay không chọn Ai vào dãy B.
Giả sử dãy đã cho là 1 8 10 2 4 6 7 Nếu ta chọn lần lượt 1, 8, 10 thì chỉ chọnđược 3 số hạng nhưng nếu bỏ qua 8 và 10 thì ta chọn được 5 số hạng 1, 2, 4, 6, 7
Khi giải một bài toán bằng cách “chia để trị” chuyển việc giải bài toán kíchthước lớn về việc giải nhiều bài toán cùng kiểu có kích thước nhỏ hơn thì thuật toánnày thường được thể hiện bằng các chương trình con đệ quy Khi đó, trên thực tế,nhiều kết quả trung gian phải tính nhiều lần
Vậy ý tưởng cơ bản của quy hoạch động là : Tránh tính toán lại mọi thứ hai lần, mà lưu giữ kết quả đã tìm kiếm được vào một bảng làm giả thiết cho việc tìm kiếm những kết quả của trường hợp sau
Chúng ta sẽ làm đầy dần giá trị của bảng này bởi các kết quả của những trườnghợp trước đã được giải Kết quả cuối cùng chính là kết quả của bài toán cần giải Nóicách khác phương pháp quy hoạch động đã thể hiện sức mạnh của nguyên lý chia đểtrị đến cao độ
Quy hoạch động là kỹ thuật thiết kế bottom-up (từ dưới lên) Nó được bắt đầu với những trường hợp con nhỏ nhất (thường là đơn giải nhất và giải được ngay) Bằng cách tổ hợp các kết quả đã có (không phải tính lại) của các trường hợp con, sẽ đạt đạt tới kết quả của trường hợp có kích thước lớn dần lên và tổng quát hơn, cho đến khi cuối cùng đạt tới lời giải của trường hợp tổng quát nhất.
Trong một số trường hợp, khi giải một bài toán A, trước hết ta tìm họ bài toán A(p) phụ thuộc tham số p (có thể p là một véc tơ) mà A(p0)=A với p0 là trạng thái ban
Trang 3đầu của bài toán A Sau đó tìm cách giải họ bài toán A(p) với tham số p bằng cách áp
dụng nguyên lý tối ưu của Bellman Cuối cùng cho p=p0 sẽ nhận được kết quả của bài
toán A ban đầu.
3 Các bước thực hiện
Bước 1: Lập hệ thức
Dựa vào nguyên lý tối ưu tìm cách chia quá trình giải bài toán thành từng giaiđoạn, sau đó tìm hệ thức biểu diễn tương quan quyết định của bước đang xử lý vớicác bước đã xử lý trước đó Hoặc tìm cách phân rã bài toán thành các “bài toán con”tương tự có kích thước nhỏ hơn, tìm hệ thức nêu quan hệ giữa kết quả bài toán kíchthước đã cho với kết quả của các “bài toán con” cùng kiểu có kích thước nhỏ hơn của
nó nhằm xây dựng phương trình truy toán (dạng hàm hoặc thủ tục đệ quy)
Về một cách xây dựng phương trình truy toán:
Ta chia việc giải bài toán thành n giai đoạn Mỗi giai đoạn i có trạng thái banđầu là t(i) và chịu tác động điều khiển d(i) sẽ biến thành trạng thái tiếp theo t(i+1) củagiai đoạn i+1 (i=1,2,…,n-1) Theo nguyên lý tối ưu của Bellman thì việc tối ưu giaiđoạn cuối cùng không làm ảnh hưởng đến kết quả toàn bài toán Với trạng thái banđầu là t(n) sau khi làm giai đoạn n tốt nhất ta có trạng thái ban đầu của giai đoạn n-1
là t(n-1) và tác động điều khiển của giai đoạn n-1 là d(n-1), có thể tiếp tục xét đếngiai đoạn n-1 Sau khi tối ưu giai đoạn n-1 ta lại có t(n-2) và d(n-2) và lại có thể tối
ưu giai đoạn n-2 … cho đến khi các giai đoạn từ n giảm đến 1 được tối ưu thì coi như
hoàn thành bài toán Gọi giá trị tối ưu của bài toán tính đến giai đoạn k là Fk, giá trị
tối ưu của bài toán tính riêng ở giai đoạn k là Gk thì
Fk = Fk-1 + Gk
Hay là: ( ( )) {Gk( ( ), ( )) 1( ( 1))} (*)
) (
k t F k d k t ax m k t
k d
Bước 2: Tổ chức dữ liệu và chương trình
Tổ chức dữ liệu sao cho đạt các yêu cầu sau:
Dữ liệu được tính toán dần theo các bước
Dữ liệu được lưu trữ để giảm lượng tính toán lặp lại
Trang 4 Kích thước miền nhớ dành cho lưu trữ dữ liệu càng nhỏ càng tốt, kiểu dữliệu được chọn phù hợp, nên chọn đơn giản dễ truy cập.
Trong một số trường hợp có thể thay mảng hai chiều với các giá trị phần tử chỉnhận giá trị 0, 1 bởi mảng hai chiều mới bằng cách dùng kỹ thuật quản lý bit
Trang 6Bước 1: Lập hệ thức
Nhận xét:
Nếu gọi F[m, v] là số cách phân tích số v thành tổng các số nguyên dương ≤ m.
Khi đó: Các cách phân tích số v thành tổng các số nguyên dương ≤ m có thể chia làmhai loại:
- Loại 1: Không chứa số m trong phép phân tích, khi đó số cách phân tích loại nàychính là số cách phân tích số v thành tổng các số nguyên dương < m, tức là sốcách phân tích số v thành tổng các số nguyên dương ≤ m - 1 và bằng F[m - 1, v]
- Loại 2: Có chứa ít nhất một số m trong phép phân tích Khi đó nếu trong các cáchphân tích loại này ta bỏ đi số m đó thì ta sẽ được các cách phân tích số v - mthành tổng các số nguyên dương ≤ m (Lưu ý: điều này chỉ đúng khi không tínhlặp lại các hoán vị của một cách) Có nghĩa là về mặt số lượng, số các cách phântích loại này bằng F[m, v - m]
Trong trường hợp m > v thì rõ ràng chỉ có các cách phân tích loại 1, còn trongtrường hợp m ≤ v thì sẽ có cả các cách phân tích loại 1 và loại 2 Vì thế:
F[m 1, v]; if m > vF[m, v]=
Bước 2: Tổ chức dữ liệu và chương trình
Ta có công thức xây dựng F[m, v] từ F[m - 1, v] và F[m, v - m] Công thức này có
tên gọi là công thức truy hồi đưa việc tính F[m, v] về việc tính các F[m', v'] với dữ
liệu nhỏ hơn Tất nhiên cuối cùng ta sẽ quan tâm đến F[n, n]: Số các cách phân tích nthành tổng các số nguyên dương ≤ n
Ví dụ với n = 5, bảng F sẽ là:
Nhìn vào bảng F, ta thấy rằng F[m, v] được tính bằng tổng của:
Trang 7Một phần tử ở hàng trên: F[m 1, v] và một phần tử ở cùng hàng, bên trái: F[m, v m].
Cải tiến dùng 2 mảng 1 chiều
Cách làm trên có thể tóm tắt lại như sau: Khởi tạo dòng 0 của bảng, sau đó dùngdòng 0 tính dòng 1, dùng dòng 1 tính dòng 2 v.v… tới khi tính được hết dòng n Cóthể nhận thấy rằng khi đã tính xong dòng thứ k thì việc lưu trữ các dòng từ dòng 0 tớidòng k - 1 là không cần thiết bởi vì việc tính dòng k + 1 chỉ phụ thuộc các giá trị lưutrữ trên dòng k Vậy ta có thể dùng hai mảng một chiều: Mảng Current lưu dòng hiệnthời đang xét của bảng và mảng Next lưu dòng kế tiếp, đầu tiên mảng Current đượcgán các giá trị tương ứng trên dòng 0 Sau đó dùng mảng Current tính mảng Next,mảng Next sau khi tính sẽ mang các giá trị tương ứng trên dòng 1 Rồi lại gán mảngCurrent := Next và tiếp tục dùng mảng Current tính mảng Next, mảng Next sẽ gồmcác giá trị tương ứng trên dòng 2 v.v… Vậy ta có cài đặt cải tiến sau:
Trang 8begin
for v := 0 to n do
if v < m then Next[v] := Current[v]
else Next[v] := Current[v] + Next[v - m];
Current := Next;
end;
WriteLn(Current[n], ' Analyses');
end.
5 Một số bài toán tối ưu giải bằng phương pháp quy hoạch động
Bài toán 1: Bài toán cái túi
Trong siêu thị có n gói hàng (n ≤ 100), gói hàng thứ i có trọng lượng là W[i] ≤
100 và trị giá V[i] ≤ 100 Một tên trộm đột nhập vào siêu thị, tên trộm mang theo mộtcái túi có thể mang được tối đa trọng lượng M (M ≤ 100) Hỏi tên trộm sẽ lấy đinhững gói hàng nào để được tổng giá trị lớn nhất
Input: file văn bản BAG.INP
- Dòng 1: Chứa hai số n, M cách nhau ít nhất một dấu cách
- n dòng tiếp theo, dòng thứ i chứa hai số nguyên dương W[i], V[i] cáchnhau ít nhất một dấu cách
Output: file văn bản BAG.OUT
- Dòng 1: Ghi giá trị lớn nhất tên trộm có thể lấy
- Dòng 2: Ghi chỉ số những gói bị lấy
Nếu gọi F[i, j] là giá trị lớn nhất có thể có bằng cách chọn trong các gói {1, 2,
…, i} với giới hạn trọng lượng j Thì giá trị lớn nhất khi được chọn trong số n gói vớigiới hạn trọng lượng M chính là F[n, M]
Công thức truy hồi tính F[i, j].
Với giới hạn trọng lượng j, việc chọn tối ưu trong số các gói {1, 2, …, i - 1, i}
để có giá trị lớn nhất sẽ có hai khả năng:
Trang 9o Nếu không chọn gói thứ i thì F[i, j] là giá trị lớn nhất có thể bằng cách chọntrong số các gói {1, 2, …, i - 1} với giới hạn trọng lượng là j Tức là F[i, j] =F[i - 1, j]
o Nếu có chọn gói thứ i (tất nhiên chỉ xét tới trường hợp này khi mà W[i] ≤ j) thìF[i, j] bằng giá trị gói thứ i là V[i] cộng với giá trị lớn nhất có thể có được bằngcách chọn trong số các gói {1, 2, …, i - 1} với giới hạn trọng lượng j - W[i].Tức là về mặt giá trị thu được: F[i, j] = V[i] + F[i - 1, j - W[i]]
Vì theo cách xây dựng F[i, j] là giá trị lớn nhất có thể, nên F[i, j] sẽ là Max trong 2giá trị thu được ở trên
Cơ sở quy hoạch động:
Dễ thấy F[0, j] = giá trị lớn nhất có thể bằng cách chọn trong số 0 gói = 0
Tính bảng phương án:
Bảng phương án F gồm n + 1 dòng, M + 1 cột, trước tiên được điền cơ sở quyhoạch động: Dòng 0 gồm toàn số 0 Sử dụng công thức truy hồi, dùng dòng 0 tínhdòng 1, dùng dòng 1 tính dòng 2, v.v… đến khi tính hết dòng n
Trang 10Bài toán 2: Chia thưởng
Cần chia hết m phần thưởng cho n học sinh sắp theo thứ tự từ giỏi trở xuống sao
cho mỗi bạn không nhận ít phần thưởng hơn bạn xếp sau mình
1 m, n 70
Hãy tính số cách chia.
Thí dụ, với số phần thưởng m = 7, và số học sinh n = 4 sẽ có 11 cách chia 7 phần
thưởng cho 4 học sinh theo yêu cầu của đầu bài Đó là:
Trang 11Gọi Chia(i, j) là số cách chia i phần thưởng cho j học sinh, ta thấy:
- Nếu không có học sinh nào (j = 0) thì không có cách chia nào (Chia = 0)
- Nếu không có phần thưởng nào (i = 0) thì chỉ có một cách chia (Chia(0,j) = 1
- mỗi học sinh nhận 0 phần thưởng) Ta cũng quy ước Chia(0, 0) = 1
- Nếu số phần thưởng ít hơn số học sinh (i < j) thì trong mọi phương án chia,
từ học sinh thứ i + 1 trở đi sẽ không được nhận phần thưởng nào:
Chia(i, j) = Chia(i, i) nếu i < j
Ta xét tất cả các phương án chia trong trường hợp i j Ta tách các phương án
chia thành hai nhóm không giao nhau dựa trên số phần thưởng mà học sinh đứng cuối
bảng thành tích, học sinh thứ j, được nhận:
- Nhóm thứ nhất gồm các phương án trong đó học sinh thứ j không được nhận
thưởng, tức là i phần thưởng chỉ chia cho j - 1 học sinh và do đó, số cách
chia, tức là số phần tử của nhóm này sẽ là: Chia(i, j - 1)
- Nhóm thứ hai gồm các phương án trong đó học sinh thứ j cũng được nhận
thưởng Khi đó, do học sinh đứng cuối bảng thành tích được nhận thưởng thìmọi học sinh khác cũng sẽ có thưởng Do ai cũng được thưởng nên ta bớt
của mỗi người một phần thưởng (để họ lĩnh sau), số phần thưởng còn lại (i j) sẽ được chia cho j học sinh Số cách chia khi đó sẽ là Chia(i - j, j)
Trang 12-Tổng số cách chia cho trường hợp i j sẽ là tổng số phần tử của hai nhóm, ta có:
Chia(i, j) = Chia(i, j - 1) + Chia(i - j, j)
Tổng hợp lại ta có:
Điều kiệni: số phần thưởngj: số học sinh
Tổ chức dữ liệu và chương trình
Ta có phương án đầu tiên của giải thuật Chia như sau:
Phương án đệ quy Hàm Chia(i,j) tính số cách chia i phần thưởng cho j học sinh
function Chia(i,j: integer):longint;
if i < j then {0 < i < j } Chia := Chia(i,i)
thưởng (m = 7) và 4 học sinh (n = 4) Thí dụ, hàm Chia(1,1) sẽ được gọi 9 lần,…
Tổng số lần gọi hàm Chia là 79 79 lần gọi hàm để sinh ra kết quả 11 là quá tốn kém
Trang 13Làm tốt
Phương án 1 khá dễ triển khai nhưng chương trình sẽ chạy rất lâu Diễn tả đệ quythường trong sáng, nhàn tản, nhưng khi thực hiện sẽ sinh ra hiện tượng gọi lặp lại những
hàm đệ quy Cải tiến là tránh những lần gọi lặp như vậy Muốn thế chúng ta tính sẵn các
giá trị của hàm theo các trị của đầu vào khác nhau và điền vào một mảng hai chiều cc Mảng cc được mô tả như sau:
const
MN = 70;{ gioi han tren cua m va n }
var cc: array[0 MN,0 MN] of longint;
Ta quy ước cc[i, j] chứa số cách chia i phần thưởng
cho j học sinh.
Theo phân tích của phương án 1, ta có:
cc[0, 0] = 1; cc[i, 0] = 0, với i:=1 m.
cc[i, j] = cc[i, i], nếu i < j
cc[i, j] = cc[i, j-1]+cc[i-j, j], nếu i j.
Từ đó ta suy ra quy trình điền trị vào bảng cc như sau:
Trang 14 với i := 0 j-1: cc[i,j] := cc[i,i];
với i := j m: cc[i,j] := cc[i,j-1]+cc[i-j,j];
Nhận kết quả: Sau khi điền bảng, giá trị cc[m, n] chính là kết quả cần tìm.
Phương án dùng mảng 2 chiều:
function Chia2(m,n: integer):longint;
var i,j: integer;
for i := j to m do cc[i,j] := cc[i,j-1]+cc[i-j,j];
end;
Chia2 := cc[m,n];
end;
Bài toán 3: Phép nhân tổ hợp dãy ma trận
Với ma trận A={a[i, j]} kích thước p×q và ma trận B={b[i, j]} kích thước q×r.Người ta có phép nhân hai ma trận đó để được ma trận C={c[i, j]} kích thước p×r.Mỗi phần tử của ma trận C được tính theo công thức:
21 100 25 14 34
9 36 9 6 14
1 1 1 1 1
1 6 1 0 3
1 5 0 1 0
0 4 2 0 1
12 11 10 9
8 7 6 5
4 3 2 1
Để thực hiện phép nhân hai ma trận A(p×q) và B(q×r) ta có thể làm như đoạn chươngtrình sau:
Trang 15Phí tổn để thực hiện phép nhân ma trận có thể đánh giá qua số lần thực hiệnphép nhân số học, với giải thuật nhân hai ma trận kể trên, để nhân ma trận A cấp pxqvới ma trận B cấp qxr ta cần thực hiện p.q.r phép nhân số học Phép nhân ma trậnkhông có tính chất giao hoán nhưng có tính chất kết hợp (A.B).C = A.(B.C)
Vậy nếu A là ma trận cấp 3x4, B là ma trận cấp 4x10 và C là ma trận cấp 10x15 thì:
- Để tính (A.B).C, phép tính (A.B) cho ma trận kích thước 3x10 sau 3.4.10=120phép nhân số, sau đó nhân tiếp với C được ma trận kết quả kích thước 3x15 sau3.10.15=450 phép nhân số Vậy tổng số phép nhân số học phải thực hiện sẽ là570
- Để tính A.(B.C), phép tính (B.C) cho ma trận kích thước 4x15 sau 4.10.15=600phép nhân số, lấy A nhân với ma trận này được ma trận kết quả kích thước3x15 sau 3.4.15=180 phép nhân số Vậy tổng số phép nhân số học phải thựchiện sẽ là 780
Vậy thì trình tự thực hiện có ảnh hưởng lớn tới chi phí Vấn đề đặt ra là tính số phítổn ít nhất khi thực hiện phép nhân một dãy các ma trận:
n i
n m m
m i
m
1
][]
2[]
1[][
Với :
m[1] là ma trận kích thước a[1] x a[2]
m[2] là ma trận kích thước a[2] x a[3]
…
m[n] là ma trận kích thước a[n] x a[n+1]
Dữ liệu: file văn bản MULTMAT.INP
- Dòng 1: Chứa số nguyên dương n ≤ 100
- Dòng 2: Chứa n + 1 số nguyên dương a[1], a[2], …, a[n+1] (∀i: 1 ≤ a[i] ≤ 100)cách nhau ít nhất một dấu cách
Kết quả: file văn bản MULTMAT.OUT
- Dòng 1: Ghi số phép nhân số học tối thiểu cần thực hiện