Tải bản đầy đủ (.pdf) (9 trang)

Phương pháp quy hoạch động trong việc giải một lớp “các bài toán tối ưu”

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (302.25 KB, 9 trang )

PHƯƠNG PHÁP QUY HOẠCH ĐỘNG TRONG VIỆC
GIẢI MỘT LỚP “CÁC BÀI TOÁN TỐI ƯU ”
L NG C HƯNG(*)

TÓM TẮT
Phương pháp quy hoạch động là một kĩ thuật được áp dụng để giải các bài toán tìm phương án tối
ưu . Vậy ý tưởng của phương pháp quy hoạch động thật đơn giản : Để tránh việc tính lại mọi thứ
hai lần , ta lưu giữ kết quả đã tìm được vào một mảng làm giả thiết cho việc tìm kiếm những kết quả
cho 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ường hợp trước đã được giải quyết . Kết quả cuối cùng cũng chính là kết quả cần giải .
Quy hoạch động là dùng kĩ thuật đi từ dưới lên . Xuất phát từ trường hợp đơn giản nhất , có thể
tìm ngay ra nghiệm bằng cách kết hợp nghiệm của chúng , ta nhận được nghiệm của bài toán cỡ
lớn hơn . Cứ như thế ta nhận được đủ nghiệm của bài toán cần tìm .Trong quá trình đi “ Từ dưới
lên “ chúng ta sẽ sử dụng một bảng lưu giữ lời giải của các bài toán con đã được giải. Khi giải một
bài toán cần đến nghiệm của những bài toán nhỏ hơn ta chỉ việc tìm kiếm trong bảng . Chính vì vậy
mà thuật toán “ Quy hoạch động “ là rất có hiệu quả .
ABSTRACT
Dynamic programming is the method of solving complex problems for optimal solutions.
The ideas behind Dynamic programming are very simple:
In order to avoid recalculating everything, we store the solutions to the subproblems in a table
which is used as a fundamental theory to solve new problems. We will build up the values of the
table by adding the solved results of the previous problems. Therefore, the solution to a given
optimal problem can be obtained by the combination of optimal solutions to its subproblems. In
another word, Dynamic programming holds the strengths of the “divide and rule” principle to high
esteem.
Dynamic programming applies the Bottom-up approach. On solving the simpler problems, we can
use the solutions to build on and arrive at solutions to bigger problems. Hence, we can formulate a
complex calculation as a recursive series of simpler calculations. In this approach, we can refer to a
tabular form of the results of the solved subproblems. When we have to find out the solutions to the
simpler subproblems ,we just look them up in the table. That is why Dynamic programming
algorithm is very efficient.


1. ĐẶT VẤN ĐỀ
Phương pháp quy hoạch động (dynamic programming) là một kĩ thuật được áp dụng để giải lớp các
bài toán , đặc biệt là các bài toán tìm phương án tối ưu như là: “các bài toán tìm giá trị nhỏ nhất hoặc
giá trị lớn nhất”.
Ta có thể giải thích ý này qua bài toán sau:

(*)

Th.S, Khoa Công nghệ thông tin, Trường Đại học Sài Gòn


Cho một dãy N số nguyên A1, A2,…,An . Hãy tìm cách xóa đi một một số í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 thứ tự xuất hiện trong dãy A [1..n] là đơn điệu. Quá trình chọn dãy B được điều khiển
qua n giai đoạn để đạt được mục tiêu là số hạng số 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ử ta có dãy: 1 8 10 2 4 6 7. Nếu ta chọn 1
8 10 thì ta chỉ có chọn được 3 số hạng nhưng nếu bỏ qua 8 10 thì ta chọn được 5 số hạng: 1 2 4 6
7. Khi giải các bài toán bằng cách “Chia để trị”, chuyển việc giải bài toán kích thướ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án nà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 được tính lại nhiều
lần. Vậy ý tưởng của phương pháp quy hoạch động thật đơn giản: để tránh việc tính lại mọi thứ hai
lần, ta lưu giữ kết quả đã tìm được vào một mảng làm giả thiết cho việc tìm kiếm những kết quả cho
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ường
hợp trước đã được giải quyết. Kết quả cuối cùng cũng chính là kết quả cần giải. Nói cá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 độ”.
2. PHƯƠNG PHÁP
Phương pháp quy hoạch động là dùng kĩ thuật đi từ dưới lên (bottom up). Xuất phát từ trường hợp
đơn giản nhất, có thể tìm ngay ra nghiệm bằng cách kết hợp nghiệm của chúng, ta nhận được
nghiệm của bài toán c lớn hơn. Cứ như thế ta nhận được đủ nghiệm của bài toán cần tìm.Trong quá
trình đi “từ dưới lên”, chúng ta sẽ sử dụng một bảng lưu giữ lời giải của các bài toán con đã được

giải mà không cần quan tâm nó được sử dụng ở đâu sau này. Khi giải một bài toán ta cần đến
nghiệm của những bài toán nhỏ hơn ta chỉ việc tìm kiếm trong bảng không cần phải giải lại. Chính
vì vậy mà thuật toán “Quy hoạch động” là rất có hiệu quả.
2.1. Ưu điểm của phương pháp quy hoạch động
- Chương trình chạy nhanh hơn.
2.2. Phạm vi hoạt động của phương pháp quy hoạch động
- Tìm nghiệm các bài toán tối ưu.
- Các bài toán có công thức truy hồi.
2.3. Hạn chế của phương pháp quy hoạch động
Phương pháp quy hoạch động không đem lại hiệu quả trong các trường hợp sau:
- Không tìm được công thức truy hồi.
- Việc tìm công thức, phương trình truy toán hoặc tìm cách phân rã bài toán nhiều khi đòi hỏi sự
phân tích tổng hợp rất công phu, dễ sai sót, khó nhận ra thế nào là thích hợp, đòi hỏi thời gian suy
nghĩ. Đồng thời không phải lúc nào cũng kết hợp việc giải của các bài toán con cũng cho kết quả
của bài toán lớn hơn.
- Khi bảng lưu trữ đòi hỏi mảng hai chiều , ba chiều …thì có thể xử lí dữ liệu với kích c mỗi chiều
lớn đến hàng trăm.
- Có những bài toán không thể giải được bằng quy hoạch động.


Như vậy, ta có thể tóm lược nguyên lí quy hoạch động do Bellman phát biểu như sau: “Quy hoạch
động là lớp các bài toán mà quyết định ở bước thứ i phụ thuộc vào các bước (i-1) trước đó” và “Một
cấu hình tối ưu thì mọi cấu hình con của nó cũng tối ưu”.
3. CẤU TRÚC CHUNG CỦA CHƯƠNG TRÌNH CHÍNH
BEGIN {Chương trình chính}
Chuẩn bị đọc dữ liệu và khởi gán một số giá trị.
Tạo bảng.
Tra bảng và ghi kết quả
END.
4. SƠ ĐỒ THUẬT TOÁN CỦA QUY HOẠCH ĐỘNG

4.1.Tính nghiệm tối ưu của bài toán trong trường hợp đơn giản nhất
4.2. Lập hệ thức
Lập hệ thức biểu thị sự tương quan quyết định của bước đang xử lí với các bước đã xử lí trước đó.
Hệ thức này là hàm đệ quy do đó sẽ gây ra tràn miền nhớ khi tổ chức thực hiện trực tiếp bằng đệ
quy. Ta còn gọi hệ thức này là phương trình đệ quy (hay phương trình truy toán).
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 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ủa giai đoạn thứ (i+1) ( i= 1,2,3,…,n).
Theo nguyên lý Bellman thì việc tối ưu giai đoạn cuối cùng không làm ảnh hưởng kết quả của bài
toán .bằng cách đi từ dưới lê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 tiếp theo cho 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 đến giai đ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 đế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 thứ 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à :
Fk(t(k))=Max{ Gk(t(k),d(k))+Fk-1(t(k-1))} (*)
 d(k)
4.3. Tổ chức dữ liệu và chương trình
- Tổ chức dữ liệu tính toán dần theo từng bước. Nên tìm cách khử đệ quy. Thông thường trong
các bài toán chúng ta hay gặp đòi hỏi một mảng một chiều, hai chiều, ba chiều.
- Tạo ra một bảng để lưu giữ các nghiệm của bài toán con. Sau đó tính nghiệm của bài toán lớn
hơn theo công thức (Hàm đệ quy) và lưu trữ vào bảng cho đến khi tìm được nghiệm của bài toán.


- Các giá trị Fk thường lưu trữ trong bảng một chiều, hai chiều, ba chiều.
- Cần lưu ý các giá trị khởi tạo ban đầu của bảng cho thích hợp, đó là kết quả của bài toán con
nhỏ nhất đang giải (trường hợp đơn giản nhất).
F1(t(1)) = Max{ G1(t(1),d(1))+F0(t(0))}

 d(1)
- Dựa vào phương trình truy toán (*) và các giá trị trong bảng để tìm dần các giá trị còn lại của
bảng.
- Ngoài ra còn cần mảng lưu trữ nghiệm tương ứng với giá trị tối ưu trong từng giai đoạn.
- Dựa vào bảng lưu trữ nghiệm và bảng giá trị tối ưu trong từng giai đã xây dựng, tìm kết quả bài
toán theo yêu cầu.
4.4. Làm tốt
Làm tốt thuật toán bằng cách thu gọn hệ thức quy hoạch động và giảm thiểu kích thước bộ nhớ.
Thường tìm cách dùng mảng một chiều thay cho mảng hai chiều nếu giá trị một dòng ( hoặc cột)
của mảng hai chiều chỉ phụ thuộc một dòng (hoặc cột) kề trước.
5. MỘT VÀI BÀI TOÁN ÁP DỤNG
B

C M HO

Cần cắm K bó hoa khác nhau vào N lọ xếp thẳng hàng sao cho hoa có số hiệu nhỏ được đặt trước
hoa có số hiệu lớn. Với mỗi loại hoa i biết giá trị thẩm mĩ khi cắm hoa đó vào lọ j là V[i][j]. Các
số liệu đều là số tự nhiên và được ghi cách nhau bởi dấu cách trên mỗi dòng .Tìm phương án cắm
hoa sao cho giá trị thẩm mĩ là cao nhất.
Dữ l ệu v

: Cho trong tập tin văn bản có tên HOA.INP có cấu trúc :

- Dòng đầu tiên là hai giá trị K và N
- Từ dòng thứ hai trở đi là các giá trị V[i][j] vớii :1..K,j :1..N ; 1KN100.
Dữ l ệu ra : Ghi vào File văn bản có tên HOA.OUT gồm hai dòng:
- Dòng đầu tiên là tổng giá trị thẩm mĩ của phương án cắm hoa tối ưu.
- Từ dòng thứ hai là hai dãy K số hiệu lọ được chọn cho mỗi bó hoa
Thí dụ:


4
1
9
7
6
Hướng giải:

HOA.INP
6
1 6 4 3
1 4 7 2
2 6 10 2
10 7 1 3

10
7
3
9

HOA.OUT
24
1 3 4 6


Ta dựng phng phỏp quy hoch ng gii bi toỏn ny:
Ta tin hnh nh sau:
5.1. Lp h thc: Gi b[i][j] l giỏ tr thm m khi cm i bú hoa vo j l, ta thy:
5.1.1. Nu s bú hoa nhiu hn s l (i>j) thỡ khụng cú cỏch cm no .
5.1.2. Nu s bú hoa bng s l thỡ ch cú mt cỏch duy nht l bú no vo l ú.
5.1.3. Ta xột trng hp s bú hoa ớt hn s l (i

+ L cui cựng, tc l th j c chn cho phng ỏn ti u.
+ L th j khụng c chn lm phng ỏn ti u .
Nu l th j c chn tc l trong l th j ta s cm bú hoa cui cựng ,bú th i .S (i-1) bú hoa u
tiờn s c phõn phi (j-1) l u tiờn, giỏ tr thm m khi ú s l :
b[i-1][j-1]+ V[i][j].
Nu l th j khụng c chn cho phng ỏn ti u thỡ i bú hoa phi c cm vo (j-1) l u tiờn
v do ú Giỏ tr thm m khi ú s l :
b[i][j-1]
T g hp l a cú g

r u kh cm bú h a v

j l s l

b[i][j] =Maxb[i -1][j -1]+V[i][j],b[i][j -1].
5.2. T chc d liu v chng trỡnh:
Nu dựng mng hai chiu b thỡ ta cú th tớnh nh trong bng di õy :

Doứng i-1
Doứng i

Coọt j -1

Coọt j

[i -1][j -1]
[i][j -1]

[i][j]


Ngoi ra ta cũn cn t trong mi ễ ca bng trờn mt mng d liu bao gm n phn t ỏnh du
l hoa no c chn cho mi tỡnh hung. Gi mng d liu ú l: L[i][j] . Ta thy nờn in bng
ln lt theo tng ct, ti mi ct ta in bng t trờn xung hoc t di lờn theo lut sau:
+ Nu b[i-1][j-1]+V[i][j]>b[i][j-1] thỡ ta phi thc hin hai thao tỏc sau :
+ t tr : b[i][j]=b[i-1][j-1]+V[i][j] v ghi nhn vic chn l hoa j trong phng ỏn mi , c th ly
phng ỏn cm hoa [i-1][j-1] ri b sung thờm vic chn l hoa j nh sau :


- Đặt L[i][j]=L[i-1][j-1] và đánh dấu phần tử thứ j trong mảng L[i][j]
+ Nếu b[i-1][j-1]+V[i][j]<=b[i][j-1] thì ta chỉ cần Copy L[i][j-1]sang L[i][j] vì ta bảo lưu phương
án [i][j-1]
5.3. Làm tốt:
Phương án dùng mảng hai chiều tốn kém bộ nhớ . Ta có thể dùng mảng một chiều T[100] xem như
một cột của bảng T nói trên . Ta duyệt j bước , tại bước j , giá trị b[i] sẽ là giá trị tối ưu khi cắm i bó
hoa vào j lọ .Như vậy tại bước thứ j ta có :
+ Nếu b[ -1] cũ +V[i][j]>b[i] cũ thì phải thực hiện hai thao tác :
- Đặt L[i]=L[i-1] cũ và đánh dấu phần tử thứ j trong mảng L[i].
+ Nếu b[ -1] cũ +V[i][j]  b[i] cũ thì ta không phải làm gì vì sẽ bảo lưu phương án cũ .Biểu thức so
sánh cho biết khi cập nhật mảng T từ bước thứ j-1 qua bước thứ j ta phải tính từ dưới lên ,nghĩa là
tính dần theo chiều giảm của i=j..1.
Để đánh dấu các lọ hoa ta dùng mảng L[MN] mỗi phần tử là một dãy 15 bit thì ta có thể đánh dấu
cho 15*8=120 lọ hoa. Khi cần đánh dấu lọ hoa thư j trong dãy L[i] ta bật bit thư j. Khi cần xem lọ
thư j có được chọn hay không ta gọi hàm Getbit.
Mô ả bằ g gô

gữ C hư sau

#include<stdio.h>
#include<conio.h>
#define max 100

#define fi "CAMHOA.INP"
#define fo "CAMHOA.OUT"
int k,n,v[max][max],b[max][max],kq,vt[max];
//-----------------------------------------------------------------------------------------------// b[i][j] là giá trị thẩm mỹ khi cắm i bó hoa vào j lọ
//-------------------------------------------------------------------------------------------------void doc(){
FILE *f =fopen(fi,"rt");
fscanf(f,"%d%d",&k,&n);
inti=0,j=0;
for(i=1;i<=k;i++)
for(j=1;j<=n;j++)
fscanf(f,"%d",&v[i][j]);
fclose(f);
}
int getmax(int i,int j) {
if(i>j)
return i;
return j;


}
/-----------------------------------------void taobang(){
for(int i=1;i<=k;i++)
for(int j=i;j<=n-(k-i);j++)
b[i][j] = getmax( b[i][j-1] , (b[i1][j1] )
//----------------------------------------void trabang(){
int i=k;j=n;
kq=b[i][j];
do {
while(b[i][j]==b[i][j-1]) j--;
vt[i]=j; i--;j--;}

while (i>-1);
}
//--------------------------------void xuat(){
FILE *f=fopen(fo,"wt");
if(kq==-1) fprintf(f,"1");
else {
trabang();
fprintf(f,"%d\n",kq);
for(inti=1;i<=k;i++)
fprintf(f,"%5d",vt[i]);
}
fclose(f);
}
//--------------------------------void main(){
doc();
if(n>=k)
taobang() ;
else kq=-1;
xuat();}
Bài toán ba lô 2:
Đề b : Cho N món hàng (N50). Món thứ i có khối lượng A[i] và có giá trị là B[i] (số nguyên). cần
chọn những món hàng nào để bỏ vào ba lô sao cho tổng giá trị của các món hàng đã chọn là lớn nhất
nhưng tổng khối lượng không vượt quá khối lượng W của ba lô cho trước (W 100). Mỗi món chỉ
chọn được một hoặc không chọn.
Dữ l ệu v

: Cho trong File BALO2.INP có cấu trúc như sau:

- Dòng đầu ghi hai số nguyên N và W
- N dòng tiếp theo, dòng thứ i ghi hai số A[i] và B[i] chỉ rõ khối lượng và giá trị của món hàng thứ I,

các số trên một dòng cách nhau ít nhất một dấu cách.


Dữ l ệu ra Ghi vào File BALO2.OUT có cấu trúc như sau:
- Dòng đầu ghi giá trị lớn nhất của bao lô cần đạt.
- Dòng thứ hai số hiệu cùng với khối lượng và giá trị của các món hàng được chọn, các số cách nhau
ít nhất một dấu cách.
Ví dụ
BALO2.OUT
BALO2.INP
4 13
16
3 4
1(3,4) 2(4,5) 3(5,6) 5(1,1)
4 5
5 6
2 3
1 1
Hướng giải: Gọi Fx[k,v] là giá trị lớn nhất của ba lô khi k món hàng đầu tiên được chọn và khối
lượng của ba lô là v.
Ta có chương trình cho hàm đệ quy như sau:
If v>=A[k] then
Fx[k][v]= Max(Fx[k-1][v-A[k]]+B[k],Fx[k-1][v])
Else
Fx[k][v]= Fx[k-1][v].
Dưới đây là bảng tính hàm đệ quy Fx[k,v] của ví dụ trên.
v

1


2

3

0
0
0
0
1

0
0
0
3
3

4
4
4
4
4

4

5

6

7


8

9

10

11

12

4
5
6
7
7

4
5
6
8
8

4
9
9
9
9

4
9

10
10
10

4
9
11
12
12

4
9
11
13
13

4
9
11
14
14

4
9
15
15
15

13


k
1
2
3
4
5

4
5
5
5
5

Ta có chương trình sau:
#include<stdio.h>
#include<conio.h>
#define max 100
#define fi "BALO2.INP"
#define fo "BALO2.OUT"
int k,n,a[max],gt[max],b[max][max],vt[max],w,kq;
void doc() {

4
9
15
15
16


FILE *f=fopen(fi,"rt"); fscanf(f,"%d%d",&n,&w); for(int i=1;i<=n;i++)

fscanf(f,"%d%d",&a[i],>[i]);fclose(f);
}
int getmax(int i,int j){
if(i>j)return i;
return j;
}
void taobang(){
int i,j; for(j=1;j<=w;j++) if(a[1]>j) b[1][j]=0;
else b[1][j]=gt[1];
for(i=2;i<=n;i++)
for(j=1;j<=w;j++) if(j>=a[i])
b[i][j] =getmax(b[i-1][j],(b[i-1][j-a[i]]+gt[i]) );
else b[i][j]=b[i-1][j];
}
void trabang(){
int d=n,c=w,tam; kq=tam=b[d][c];
do{ while(b[d][c]==b[d-1][c]) d--; while(b[d][c]==b[d][c-1]) c--;
vt[d]=1;c-=a[d];
}while(c>0);
}
void xuat(){
FILE *f=fopen(fo,"wt");
trabang();
fprintf(f,"%d",kq);
for(int i=1;i<=n;i++)
if(vt[i])
fprintf(f,"\n %3d (%d,%d)",i,a[i],gt[i]);
fclose(f);
}
void main(){

doc();
taobang();
xuat();
Bài báo nêu lên phương pháp quy hoạch động chung và một vài áp dụng để giải các bài toán tối ưu,
một phương pháp được sử dụng nhiều trong dạy chuyên tin học. Trong khuôn khổ của tạp chí người
viết chỉ mới giới thiệu một phần nhỏ của việc áp dụng này.
TÀI LIỆU THAM KHẢO
1. Niclaus Wirth (1981), Bản dịch Algorithms+Data structure, NXB Thống Kê.
2. Đỗ Xuân Lôi (1996), Cấu Trúc dữ liệu và giải thuật, NXB Khoa học Kỹ Thuật.
3. Hoàng Kiếm (Chủ biên)( 1996), Giáo trình cấu trúc dữ liệu, ĐH Khoa Học Tự Nhiên.
4. Nguyễn Quốc Cường và Hoàng Đức Hải(1995), Cấu trúc dữ liệu + Giải Thuật = Chương Trình,
NXB Giáo Dục.
5. Tin học và nhà trường 6 năm (1999-2005)



×