Tải bản đầy đủ (.doc) (34 trang)

Tiểu luận môn thuật toán nâng cao

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 (2.3 MB, 34 trang )

Tiểu luận Thuật toán nâng cao

GV hướng dẫn : TS Nguyễn Duy Phương

MỤC LỤC
TÀI LIỆU THAM KHẢO.....................................................................................33

Học viên: Ngô Ngọc Thắng – Lớp HTTT 2016 - 1

1


Tiểu luận Thuật toán nâng cao

GV hướng dẫn : TS Nguyễn Duy Phương

MỞ ĐẦU
Thuật toán F giải bài toán P là dãy các thao tác sơ cấp F1, F2,..,FN trên
tập dữ kiện đầu vào (Input) để đưa ra được kết quả ra (Output). F1 F2. .FN
(Input) →Ouput.
• F = F1 F2.. FN được gọi là thuật toán giải bài toán P. Trong đó, mỗi
Fi chỉ là các phép tính toán số học hoặc logic.
• Input được gọi là tập dữ kiện đầu vào (dữ liệu đầu vào).
• Output là kết quả nhận được sau khi thực hiện thuật toán F trên tập
Input. Một thuật toán cần thỏa mãn các tính chất dưới đây:
• Tính đơn định. Ở mỗi bước của thuật toán, các thao tác sơ cấp phải
hết sức rõ ràng, không gây nên sự lộn xộn, nhập nhằng, đa nghĩa. Thực hiện
đúng các bước của thuật toán trên tập dữ liệu vào, chỉ cho duy nhất một kết
quả ra.
• Tính dừng. Thuật toán không được rơi vào quá trình vô hạn. Phải
dừng lại và cho kết quả sau một số hữu hạn các bước.


• Tính đúng. Sau khi thực hiện tất cả các bước của thuật toán theo
đúng qui trình đã định, ta phải nhận được kết quả mong muốn với mọi bộ dữ
liệu đầu vào. Kết quả đó được kiểm chứng bằng yêu cầu của bài toán.
•Tính phổ dụng. Thuật toán phải dễ sửa đổi để thích ứng được với bất
kỳ bài toán nào trong lớp các bài toán cùng loại và có thể làm việc trên nhiều
loại dữ liệu khác nhau.
• Tính khả thi. Thuật toán phải dễ hiểu, dễ cài đặt, thực hiện được trên
máy tính với thời gian cho phép.
Trong tiểu luận này, tác giả trình bày 8 thuật toán gồm: Đệ quy, vét cạn
(duyệt), quy hoạch động, nhánh cận, sinh, quay lui, tham lam, chia và trị. Mỗi
thuật toán có 01 ví dụ cụ thể về cách sử dụng thuật toán đó để giải quyết bài
toán.
Do thời gian có hạn, lượng kiến thức còn hạn hẹp, tác giả rất mong
nhận được sự góp ý để tiểu luận được hoàn thiện hơn.
Học viên: Ngô Ngọc Thắng – Lớp HTTT 2016 - 1

2


Tiểu luận Thuật toán nâng cao

GV hướng dẫn : TS Nguyễn Duy Phương

I. PHƯƠNG PHÁP ĐỆ QUY
1.1. Khái lược về phương pháp đệ quy
Phương pháp định nghĩa bằng đệ qui: Một đối tượng được định
nghĩa trực tiếp hoặc gián tiếp thông qua chính nó được gọi là phép định nghĩa
bằng đệ qui.
Thuật toán đệ qui: Thuật toán giải bài toán P trực tiếp hoặc gián tiếp
thông qua bài toán P’ giống như P được gọi là thuật toán đệ qui. Một hàm

được gọi là đệ qui nếu nó được gọi trực tiếp hoặc gián tiếp đến chính nó. Một
bài toán giải được bằng đệ qui nếu nó thỏa mãn hai điều kiện:
• Phân tích được: Có thể giải được bài toán P bằng bài toán P’ giống
như P và chỉ khác P ở dữ liệu đầu vào. Việc giải bài toán P’ cũng được thực
hiện theo cách phân tích giống như P.
• Điều kiện dừng: Dãy các bài toán P’ giống như P là hữu hạn và sẽ
dừng tại một bài toán xác định nào đó.
Thuật toán đệ qui tổng quát có thể được mô tả như sau:
Thuật toán Recursion ( P ) {
1. Nếu P thỏa mãn điều kiện dừng:
<Giải P với điều kiện dừng>;
2. Nếu P không thỏa mãn điều kiện dừng:
Recursion(P’).
}
1.2. Giải quyết bài toán cụ thể bằng phương pháp đệ quy
1.2.1. Phát biểu bài toán
Cho 2 số nguyên dương a và b, viết chương trình tìm ƯCLN (a,b).
1.2.2 Trình bày thuật toán
Ước số chung lớn nhất (ƯCLN) của 2 số nguyên dương a, b là 1 số k
lớn nhất sao cho a và b đều chia hết cho k. Một phương pháp đơn giản nhất
để tìm ƯCLN của a và b là duyệt từ số nhỏ hơn trong 2 số a, b cho đến 1,
Học viên: Ngô Ngọc Thắng – Lớp HTTT 2016 - 1

3


Tiểu luận Thuật toán nâng cao

GV hướng dẫn : TS Nguyễn Duy Phương


ngay khi gặp số nào đó mà a và b đều chia hết cho nó thì đó chính là ƯCLN
của a, b. Tuy nhiên, phương pháp này không phải là cách tìm ƯCLN hiệu
quả. Cách đây hơn 2000 năm, Euclid đã phát minh ra một giải thuật tìm
ƯCLN của 2 số nguyên dương a, b rất hiệu quả. Ý tưởng cơ bản của thuật
toán này cũng tương tự như ý tưởng đệ qui, tức là đưa bài toán về 1 bài toán
đơn giản hơn. Cụ thể, giả sử a lớn hơn b, khi đó việc tính ƯCLN của a và b
sẽ được đưa về bài toán tính ƯCLN của a mod b và b vì ƯCLN(a,b) =
ƯCLN(a mod b, b).
Thuật toán:
int UCLN(int m, int n){
if (n==0) return m;
else return UCLN(n,m%n);
}
Điểm dừng của thuật toán là khi n=0. Khi đó đương nhiên là ƯCLN
của m và 0 chính là m, vì 0 chia hết cho mọi số. Khi n khác 0, lời gọi đệ qui
ƯCLN(n, m% n) được thực hiện. Chú ý rằng ta giả sử m >= n trong thủ tục
tính ƯCLN, do đó, khi gọi đệ qui ta gọi ƯCLN (n, m% n) để đảm bảo thứ tự
các tham số vì n bao giờ cũng lớn hơn phần dư của phép m cho n. Sau mỗi lần
gọi đệ qui, các tham số của thủ tục sẽ nhỏ dần đi, và sau 1 số hữu hạn lời gọi
tham số nhỏ hơn sẽ bằng 0. Đó chính là điểm dừng của thuật toán.
1.2.3. Kiểm nghiệm thuật toán
Kiểm nghiệm thuật toán để tính ƯCLN của 108 và 45, ta gọi thủ tục
ƯCLN(108, 45). Khi đó, các thủ tục sau sẽ lần lượt được gọi:
ƯCLN(108, 45) 108 chia 45 dư 18, do đó tiếp theo gọi
ƯCLN(45, 18) 45 chia 18 dư 9, do đó tiếp theo gọi
ƯCLN(18, 9) 18 chia 9 dư 0, do đó tiếp theo gọi
ƯCLN(9, 0) tham số thứ 2 = 0, do đó kết quả là tham số thứ nhất, tức là 9.
Học viên: Ngô Ngọc Thắng – Lớp HTTT 2016 - 1

4



Tiểu luận Thuật toán nâng cao

GV hướng dẫn : TS Nguyễn Duy Phương

Như vậy, ta tìm được ƯCLN của 108 và 45 là 9 chỉ sau 4 lần gọi thủ tục.
1.2.4. Mã hóa thuật toán
#include <conio.h>
#include <stdio.h>
int USCLN(int a,int b){
if (b==0) return a;
else
return USCLN(b,a%b);
}
int main(void){
int a,b;
printf("Nhap a: "); scanf("%d",&a);
printf("Nhap b: "); scanf("%d",&b);
printf("USCLN(%d,%d)=%3d",a,b,USCLN(a,b));
getch();
}

1.2.5. Kết quả chạy chương trình

II. PHƯƠNG PHÁP DUYỆT (VÉT CẠN)
2.1. Khái lược về phương pháp vét cạn
Vét cạn là phương pháp tìm nghiệm của bài toán bằng cách xem xét tất
cả các phương án có thể xảy ra của bài toán. Trong đó:
Sử dụng các công cụ toán học:

• Sử dụng các định lý, mệnh đề, lập luận và suy logic của trong toán
học để tìm ra nghiệm của bài toán.
• Ưu điểm: dễ dàng máy tính hóa các bài toán đã có thuật giải
(MathLab).
Học viên: Ngô Ngọc Thắng – Lớp HTTT 2016 - 1

5


Tiểu luận Thuật toán nâng cao

GV hướng dẫn : TS Nguyễn Duy Phương

• Nhược điểm: chỉ thực hiện được trên lớp các bài toán đã có thuật giải.
Lớp bài toán này rất nhỏ so với lớp các bài toán thực tế.
Sử dụng máy tính và các công cụ tính toán:
• Giải được mọi bài toán đã có thuật giải bằng máy tính.
• Đối với một số bài toán chưa có thuật giải, ta có thể sử dụng máy tính
để xem xét tất cả các khả năng có thể để từ đó đưa ra nghiệm của bài toán.
Một thuật toán duyệt cần thỏa mãn hai điều kiện:
• Không được lặp lại bất kỳ khả năng nào.
• Không được bỏ sót bất kỳ cấu hình nào.
2.2. Giải quyết bài toán cụ thể bằng phương pháp vét cạn
2.2.1. Phát biểu bài toán
Trình bày thuật toán và viết chương trình kiểm tra một số nguyên dương a có
phải là số nguyên tố hay không?
2.2.2. Trình bày thuật toán
Số nguyên tố là các số nguyên dương chỉ chia hết cho 1 và chính nó. Vậy để
kiểm tra 1 số a có phải là số nguyên tố hay không chúng ta kiểm tra xem a có
chia hết cho số nào khác ngoài 1 và a hay không. Nếu tồn tại ít nhất một phép

chia hết cho số khác 1 và a thì a không phải là số nguyên tố.
Ta sử dụng phương pháp duyệt (vét cạn) để giải quyết bài toán. Vậy
chiến thuật vét cạn cho bài toán này là gì?
Nếu a ==1 hoặc a==2 thì a là số nguyên tố; trong trường hợp a>2
chúng ta thực hiện phương pháp lấy a chia cho các số từ 2 đến a – 1, nếu tồn
tại một giá trị mà a chia hết trong khoảng từ 2 đến a -1 thì a không phải là số
nguyên tố. Với cách này thì vòng for sẽ phải chạy từ 2 đến a -1;
Suy nghĩ thêm một chút chúng ta thấy không cần thiết phải kiểm tra a
có chia hết cho tất cả các số từ 2 đến a – 1 mà chỉ cần kiểm tra xem a có chia
hết số nào trong khoảng từ 2 đến a/2 thôi. Vì a chia cho 1 số lớn hơn a/2 đến a
-1 thì kết quả nhận được luôn lớn hơn 1 và nhỏ hơn 2. Do đó a không chia hết
cho các số lớn hơn a/2. Vì vậy chúng ta không cần phải xét các trường hợp
Học viên: Ngô Ngọc Thắng – Lớp HTTT 2016 - 1

6


Tiểu luận Thuật toán nâng cao

GV hướng dẫn : TS Nguyễn Duy Phương

thuộc khoảng từ a/2 đến a-1. Số phép toán duyệt sẽ ít hơn so với phương pháp
trên.
Tuy nhiên phương pháp này vẫn chưa tối ưu vì a chỉ có thể chia hết cho
số lớn nhất bằng

(trong trường hợp

là số nguyên). Do vậy ta chỉ cần xét


xem a có chia hết cho các số thuộc khoảng từ 2 đến

thôi. Số phép toán

phải thực hiện sẽ được giảm thiểu hơn so với phương pháp duyệt xem a có
chia hết cho các số từ 2 đến a/2. Do vậy thuật toán để kiểm tra a có phải là số
nguyên tố không như sau:
int KTNT(int a){
if(a<=2) return 1;
else{
for(int i=2;i<=sqrt(a);i++)
if(a%i==0) return 0;
return 1;
}
}

2.2.3. Kiểm nghiệm thuật toán
Với a = 11
b = {2->

}

a b?

Số ước

2

No


0

3

No

0

{1,a}

Kết quả trả về: 11 là số nguyên tố
2.2.4. Mã hóa thuật toán
#include <conio.h>
#include <stdio.h>
#include <math.h>
int KTNT(int a){
if(a<=2) return 1;
else{
for(int i=2;i<=sqrt(a);i++)
if(a%i==0) return 0;
return 1;
}
}

Học viên: Ngô Ngọc Thắng – Lớp HTTT 2016 - 1

7


Tiểu luận Thuật toán nâng cao


GV hướng dẫn : TS Nguyễn Duy Phương

int main(void){
int a;
printf("Nhap a: "); scanf("%d",&a);
if(KTNT(a)) printf("%d là so nguyen to",a);
else printf("%d khong la so nguyen to",a);
getch();
}

2.2.5. Kết quả thực hiện chương trình

III. PHƯƠNG PHÁP QUY HOẠCH ĐỘNG
3.1. Khái lược về phương pháp quy hoạch động
Phương pháp qui hoạch động dùng để giải lớp các bài toán thỏa mãn
những điều kiện sau:
• Bài toán lớn cần giải có thể phân rã được thành nhiều bài toán con.
Trong đó, sự phối hợp lời giải của các bài toán con cho ta lời giải của bài toán
lớn. Bài toán con có lời giải đơn giản được gọi là cơ sở của qui hoạch động.
Công thức phối hợp nghiệm của các bài toán con để có nghiệm của bài toán
lớn được gọi là công thức truy hồi của qui hoạch động.
• Phải có đủ không gian vật lý lưu trữ lời giải các bài toán con (Bảng
phương án của qui hoạch động). Vì qui hoạch động đi giải quyết tất cả các bài
toán con, do vậy nếu ta không lưu trữ được lời giải các bài toán con thì không
thể phối hợp được lời giải giữa các bài toán con.
• Quá trình giải quyết từ bài toán cơ sở (bài toán con) để tìm ra lời giải
bài toán lớn phải được thực hiện sau hữu hạn bước dựa trên bảng phương án
của qui hoạch động.
3.2. Giải quyết bài toán cụ thể bằng phương pháp quy hoạch động

3.2.1. Phát biểu bài toán
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

Học viên: Ngô Ngọc Thắng – Lớp HTTT 2016 - 1

8


Tiểu luận Thuật toán nâng cao

GV hướng dẫn : TS Nguyễn Duy Phương

trộm mang theo một cá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 đi những gói hàng nào để được tổng giá trị lớn nhất.
Input:File văn bản BAICAITUI.txt
+ Dòng 1: Chứa 2 số n, M cách nhau ít nhất một dấu cách
+ n dòng tiếp theo: dòng i chứa 2 số nguyên dương W[i], V[i] cách
nhau ít nhất một dấu cách.
Output: Tệp văn bản KETQUA.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
BAICAITUI.TXT
5
11
3
3
4
4
5

4
9
10
4
4
3.2.2 Trình bày thuật toán

KETQUA.OUT
11
5 2 1

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 chọn được trong
số n gói giớ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 các gói {1 ,2,..., i-1,
i} để có giá trị lớn nhất sẽ có 2 khả năng:
+ Nếu không chọn gói thứ i thì F[i, j] là giá trị lớn nhất có thể có bằng
cách chọn trong số các gói { 1,2,…, i -1 } với giới hạn trọng lượng j. Tức
là:F[i,j] = F[i-1,j];
+ Nếu có chọn gói thứ i (tất nhiên chỉ xét trong 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ó bằng cá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]];

Học viên: Ngô Ngọc Thắng – Lớp HTTT 2016 - 1

9



Tiểu luận Thuật toán nâng cao

GV hướng dẫn : TS Nguyễn Duy Phương

Vì theo cách xây dựng F[i,j] là giá trị lớn nhất có thể có, nên F[i,j] sẽ là
max trong 2 giá 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ể có bằng cách chọn 0 gói = 0.
Bảng tính phương án
Bằng phương án F gồm n+1 dòng, M+1 cột, trước tiên ta điền cơ sở
quy hoạ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ính dòng 1, dùng dòng 1
tính dòng 2,….. đến khi hết dòng n.

Truy vết
Tính xong bảng phương án thì ta quan tâm đến F[n,M] đó chính là giá
trị lớn nhất thu được khi chọn trong cả n gói với giới hạn trọng lượng M.
Nếu F[n,M] = F[n-1,M] thì tức là không chọn gói thứ n, ta truy tiếp
F[n-1,M]. Còn nếu F[n,M] khác F[n-1,M] thì ta thông báo rằng phép chọn tối
ưu có chọn gói thứ n và truy tiếp F[n-1,M-W[n]]. Cứ tiếp tục cho tới khi lên
tới bảng 0 của phương án.
=> Thuật toán:
// Giai thuat quy hoach dong cho bai toan cai tui
Optimize; //Phương án công thức truy hồi
var
i,j: Integer;
begin
FillChar(F[0], SizeOf(F[0],0);
for i:= 1 to n do

for j:= 0 to M do

Học viên: Ngô Ngọc Thắng – Lớp HTTT 2016 - 1

10


Tiểu luận Thuật toán nâng cao

GV hướng dẫn : TS Nguyễn Duy Phương

begin
F[i,j]:=F[i-1,j];//giả
F[i,j]=F[i-1,j]
if ( j>= W[i] and (F[i,j]
F[i,j] := F[i-1,j-W[i]]
//Sau do danh gia: nếu chọn gói i
end;
end;

sử

không

chọn

gói

i


thì

< F[i-1,j-W[i]]+V[i]) then
+ V[i];
lợi hơn thì đặt lại F[i,j]

procedure Trace; //Truy vết tìm nghiệm tối ưu
var
fo: Text;
begin
Assign(fo, OutputFile); Rewrite(fo);
WriteLn(fo, F[n,M]); // In ra gía trị lớn nhất
while n<>0 do //Truy vết phương án từ hàng n lên hàng 0
begin
if F[n,M] <> F[n-1,M] then//Nếu có thể chọn gói thứ n
begin
Write(fo,n,' ');
M:=M-W[n];//chọn gói thứ n rồi thì chỉ có thể
mang M-W[n]
end;
Dec(n);
Close(fo);
end;

3.2.3. Kiểm nghiệm thuật toán
Ta có bảng phương án:
n\M
0
1
2

3
4
5

0
0
0
0
0
0
0

1
0
0
0
0
0
0

2
0
0
0
0
0
0

3
0

3
3
3
3
3

4
0
3
4
4
4
4

5
0
3
4
4
4
4

6
0
3
4
4
4
4


7
0
3
7
7
7
7

8
0
3
7
7
7
7

9
0
3
7
8
10
10

10
0
3
7
8
10

10

11
0
3
7
8
10
11

Chúng ta có thể chọn các vật: 5, 2, 1 với giá trị lớn nhất là 11
3.2.4. Mã hóa bài toán bằng phương pháp quy hoạch động
#include <conio.h>
#include<stdio.h>
#define MAX 100
FILE *fp;
int n,m; // n la so do vat va m la khoi luong toi da ma tui
mang duoc

Học viên: Ngô Ngọc Thắng – Lớp HTTT 2016 - 1

11


Tiểu luận Thuật toán nâng cao

GV hướng dẫn : TS Nguyễn Duy Phương

int X[MAX],Y[MAX];//X[i],Y[i]de luu khoi luong va gia tri
cua vat thu i

int F[MAX][MAX];
//F[i][j] la tong gia tri lon nhat ma tui chua duoc neu
lay tu 1-i vat
// voi khoi luong gioi han la j
void Readdata(){
fp=fopen("BAICAITUI.txt","r");
fscanf(fp,"%d%d",&n,&m);
for(int i=1;i<=n;i++)
fscanf(fp,"%d%d",&X[i],&Y[i]);
for( int i =1;i<=n;++i)
F[i][0] = 0;
}
void Optimize(){
for(int i=1;i<=n;++i)
for(int j=0;j<=m;++j){
F[i][j]=F[i-1][j];
//Gia su chi chon duoc den vat thu i
if(j>=X[i]&&F[i][j]F[i][j]=F[i-1][j-X[i]]+Y[i];
}
}
void Trace(){ //Truy vet
fp=fopen("KETQUA.OUT","w");
fprintf(fp,"Gia tri Max = %d\n",F[n][m]);
while (n!=0){
if(F[n][m]!=F[n-1][m]){
fprintf(fp,"%5d",n);// Co the chon duoc vat thu n
m=m-X[n]; // Da chon vat thu n thi chi con mang duoc mY[n] khoi luong
} --n;
} fclose(fp);

}
int main(void){
Readdata();
Optimize();
Trace();
getch();
}

3.2.4. Kết quả thực hiên chương trình

Học viên: Ngô Ngọc Thắng – Lớp HTTT 2016 - 1

12


Tiểu luận Thuật toán nâng cao

GV hướng dẫn : TS Nguyễn Duy Phương

IV. PHƯƠNG PHÁP NHÁNH CẬN
4.1. Khái lược về phương pháp nhánh cận
Phương pháp (hay giải thuật) nhánh cận là một trong những phương
pháp giải các bài toán liệt kê cấu hình có điều kiện tối ưu.
Bài toán tối ưu
- Bài toán yêu cầu tìm ra một phương án tốt nhất thỏa mãn một số yêu
cầu ràng buộc nào đó – nghiệm của bài toán đạt giá trị max/min trong không
gian nghiệm.
- Thuộc lĩnh vực Tối ưu toán học hoặc Quy hoạch toán học. Lời giải
toán có thể khó => Sự vào cuộc của Tin học.
- Hai hướng tiếp cận tìm lời giải tối ưu cho bài toán:

+ Tìm từng lời giải, khi hoàn tất một lời giải thì so sánh của nó với chi
phí tốt nhất hiện có. Nếu tốt hơn thì cập nhật chi phí tốt nhất mới.
+ Với mỗi lời giải, khi xây dựng các thành phần nghiệm luôn kiểm tra
điều kiện nếu đi tiếp theo hướng này thì có khả năng nhận được lời giải tốt
hơn lời giải hiện có không? Nếu không thì không đi theo hướng này nữa.
Nguyên lý nhánh cận
- Nhánh cận (Branch and Bound): Thuật toán tìm lời giải cho các bài
toán tối ưu dạng liệt kê cấu hình dựa trên nguyên lí đánh giá nhánh cận.
- Nguyên lí đánh giá nhánh cận: Sử dụng các thông tin đã tìm được
trong lời giải của bài toán để loại bỏ sớm phương án không dẫn tới lời giải tối
ưu.
Bản chất:
+ Sử dụng phương pháp quay lui nhưng tại mỗi bước đưa thêm thao tác
đánh giá giá trị phương án hiện có.
+ Nếu đó là phương án tối ưu hoặc có hy vọng trở thành phương án tối
ưu (tức là tốt hơn phương án hiện có) thì cập nhật lại phương án tối ưu hoặc
đi tiếp theo hướng đó.
+ Trong trường hợp ngược lại thì bỏ qua hướng đang xét.
Học viên: Ngô Ngọc Thắng – Lớp HTTT 2016 - 1

13


Tiểu luận Thuật toán nâng cao

GV hướng dẫn : TS Nguyễn Duy Phương

4.2. Giải quyết bài toán cụ thể bằng phương pháp nhánh cận
4.2.1. Phát biểu bài toán
Bài toán cái túi 0-n.

Có một tên trộm mang theo một cái túi có thể đựng trọng lượng tối đa
là M vào một cửa hàng có n loại đồ vật, mỗi loại đồ vật có một giá trị (v) và
trọng lượng (w) riêng (giả sử số lượng mỗi đồ vật là rất nhiều). Hỏi tên trộm
chọn những loại đồ vật nào với số lượng bao nhiêu để tổng trọng lượng không
vượt quá trọng lượng tối đa của cái túi (M) và tổng giá trị của các đồ vật (V)
là lớn nhất ?
4.2.2. Trình bày thuật toán
Áp dụng kỹ thuật nhánh cận để giải bài toán.
Ký hiệu : w[i] là trọng lượng của loại đồ vật i. (i = 1..n). v[i] là giá trị
của loại đồ vật i. (i = 1..n). Mô hình bài toán có thể đưa về dạng bài toán quy
hoạch tuyến tính nguyên tìm vectơ x = [ x1 , .. , xn ] ( với x1, .. , , xn là số
lượng loại đồ vật 1,..,n được chọn ) như sau:
Hàm mục tiêu : f = v1*x1 + … + vn*xn  max. Hệ ràng buộc :
w1*x1 + .. + wn*xn ≤ M
xi ≥ 0 ( i =1...n)
Để đánh giá giá trị của đồ vật so với trọng lượng ta dùng hệ số giá trị :
vi/wi ( i = 1..n). Ưu tiên các đồ vật có hệ số giá trị lớn chọn trước nên ta sắp
xếp các loại đồ vật từ cao đến thấp theo hệ số giá trị.
v1/w1 ≥ v2/w2 ≥ .. ≥ vn/wn.
Dễ thấy giá trị tối đa của cái túi f(x) ≤ M*v1/w1. Vậy x1 có thể nhận
các giá trị trong tập S1 = {k1 | k1 ≤ M/w1}.
Ứng với mỗi giá trị x1 = k1  S ta thiết lập một nhánh phương án có
cập trên là: k1*v1 + ( M – k1*w1 )* v2/w2.
Và trong nhánh này, x2 có thể nhận các giá trị trong tập: S2 = {k2 |
k2*w2 ≤ ( M – k1*w1)}
Ứng với mỗi giá trị x2 = k2 ∈ S2 ta lại thiết lập một nhánh phương án
có cận trên là : k1*v1 + k2*v2 + ( M – k1*w1 - k2*w2)* v3/w3.
Học viên: Ngô Ngọc Thắng – Lớp HTTT 2016 - 1

14



Tiểu luận Thuật toán nâng cao

GV hướng dẫn : TS Nguyễn Duy Phương

Tương tự quá trình thiết lập nhánh và cận trên cứ lặp lại cho tới khi
nào không thể chọn thêm một đồ vật nào nữa. (túi không thể chứa thêm đồ
vật nào nữa). Để tìm phương án tối ưu ta đi theo nhánh có cận lớn nhất. Ký
hiệu : a : giá trị đồ vật đang đạt được, b: cận trên của nhánh, M’ là trọng
lượng còn lại. Bài toán biểu diễn theo cấu trúc cây như sau:
Tất cả phương án
a=0, M’=M
b=M’*v1/w1

x1=k1

a=a+v1*k1
M’=M’-w1*k1
b=a+M’*v2/w2

…..

….

…..

x2=k2

a=a+v2*k2

M’=M’-w2*k2
b=a+M’*v3/w3

….

…..

…………………………………
Thuật toán:
Input: n, m; //số vật và trọng lượng tối đa túi có thể mang
W[i], V[i]; //Trọng lượng và giá trị của vật thứ i;
wrem; Trọng lượng túi còn có thể chứa;
vsel, wsel; Giá trị sử dụng và trọng lượng túi đang chứa;
Output: FOPT; //Giá trị tối đa
XOPT; //Phương án tương ứng
Int X[MAX],XOPT[MAX],W[MAX],V[MAX],n,m,wrem,a=0,vsel=0,wsel=0,FOPT=0;

float b;
void Try(int i){
for(int j=0;j<=wrem/(float)W[i];j++){
b=vsel+j*V[i]+(wrem-j*W[i])*V[i+1]/(float)W[i+1];
if(b>=FOPT){
X[i]=j;
vsel=vsel+j*V[i];

Học viên: Ngô Ngọc Thắng – Lớp HTTT 2016 - 1

15



Tiểu luận Thuật toán nâng cao

GV hướng dẫn : TS Nguyễn Duy Phương

wsel=wsel+j*W[i];
wrem=wrem-j*W[i];
if((i==n)||(wrem<=0)) Update();
else
Try(i+1);
X[i]=0;
vsel=vsel-j*V[i];
wsel=wsel-j*W[i];
wrem=wrem+j*W[i];
}}}

4.2.3. Kiểm nghiệm thuật toán
Với n =4, m=8
i

=1

2

3 4

W[i]

=5

3


2 4

V[i]

= 10 5 3 6
Sắp xếp theo thứ tự giảm dần của v[i]/w[i].

i

=1

2

3

4

W[i]

=5

3

2

4

V[i]


= 10

5

3

6

V[i]/W[i] = 2
5/3 3/2 3/2
Bài toán này biểu diễn theo cấu trúc cây như sau. (Chọn nhánh có cận
lớn hơn để theo).
Tất cả các phương án
a=0, M’=8, b=8*2=16
x1 =1

x1 =0

a=0+10*1=10;
M’=8-5*1=3;
b=10+8*5/3=15.

a=0+10*0=0;
M’=8-5*0=8;
b=0+8*5/3=13.3.

x2 =1

x2 =0


a=10+5*0=10;
M’=3-3*0=3;
b=10+3*3/2=14.5.

Học viên: Ngô Ngọc Thắng – Lớp HTTT 2016 - 1

a=10+5*1=15;
M’=3-3*1=0;
b=15+0*15=15.
16


Tiểu luận Thuật toán nâng cao

GV hướng dẫn : TS Nguyễn Duy Phương

Ta thấy khi x2 = 1 thì M’ = 0 không thể chọn được đồ vật nào nữa. Nên
nhánh này dừng lại và phương án tốt nhất của nhánh này là x = [1,1,0,0] và có
giá trị là 15.
Vì ta đã đi theo cận lớn nhất nên phương án x = [1,1,0,0] là phương án
tối ưu.
Giá trị tối ưu mà túi mang được là 15.
4.2.4. Mã hóa thuật toán
//Bai toan cai tui ky thuat nhanh can
#include<stdio.h>
#include<conio.h>
#define MAX 100
int X[MAX],XOPT[MAX],W[MAX],V[MAX],n,m,wrem,a=0,vsel=0,wsel=0,FOPT=0;

float b;

FILE *fp;
void Init(){
fp=fopen("BAICAITUI.txt","r");
fscanf(fp,"%d%d\n",&n,&m);
for(int i=1;i<=n;i++)
fscanf(fp,"%d%d",&W[i],&V[i]);
wrem=m;
b=wrem*V[1]/(float)W[1];
fclose(fp);
}
void Result(){
printf("%d\n",FOPT);
for(int i=1;i<=n;i++)
printf("%3d",XOPT[i]);
}
void Update(){
if(FOPTFOPT=vsel;
for(int i=1;i<=n;i++)
XOPT[i]=X[i];
}
}
void Try(int i){
for(int j=0;j<=wrem/(float)W[i];j++){
b=vsel+j*V[i]+(wrem-j*W[i])*V[i+1]/(float)W[i+1];
if(b>=FOPT){
X[i]=j;
vsel=vsel+j*V[i];

Học viên: Ngô Ngọc Thắng – Lớp HTTT 2016 - 1


17


Tiểu luận Thuật toán nâng cao

GV hướng dẫn : TS Nguyễn Duy Phương

wsel=wsel+j*W[i];
wrem=wrem-j*W[i];
if((i==n)||(wrem<=0)) Update();
else
Try(i+1);
X[i]=0;
vsel=vsel-j*V[i];
wsel=wsel-j*W[i];
wrem=wrem+j*W[i];
}}}
int main(void){
Init();
Try(1);
Result();
getch();
}

4.2.5. Kết quả thực hiện chương trình

V. THUẬT TOÁN SINH
5.1. Khái lược về thuật toán sinh
Thuật toán sinh được dùng để giải lớp các bài toán thỏa mãn hai điều

kiện:
•Xác định được một thứ tự trên tập các cấu hình cần liệt kê của bài
toán. Biết được cấu hình đầu tiên, biết được cấu hình cuối cùng.
•Từ một cấu hình cuối cùng, ta xây dựng được thuật toán sinh ra cấu
hình đứng ngay sau nó theo thứ tự.
Thuật toán:
Thuật toán Generation:
Bước1 (Khởi tạo):
<Thiết lập cấu hình đầu tiên>;
Bước 2 (Bước lặp):
while (<Lặp khi cấu hình chưa phải cuối cùng>) do
<Đưa ra cấu hình hiện tại>;
<Sinh ra cấu hình kế tiếp>;
Học viên: Ngô Ngọc Thắng – Lớp HTTT 2016 - 1

18


Tiểu luận Thuật toán nâng cao

GV hướng dẫn : TS Nguyễn Duy Phương

endwhile;
End.
5.2. Giải quyết bài toán cụ thể bằng thuật toán sinh
5.2.1. Phát biểu bài toán
Có một tên trộm mang theo một cái túi có thể đựng trọng lượng tối đa
là M vào một cửa hàng có N loại đồ vật, mỗi loại đồ vật có trọng lượng a
riêng và một giá trị c (giả sử số lượng mỗi đồ vật chỉ có một). Hỏi tên trộm
chọn những loại đồ vật nào để tổng trọng lượng không vượt quá trọng lượng

tối đa của cái túi (B) và tổng giá trị của các đồ vật (F) là lớn nhất ?
5.2.2. Trình bày thuật toán
Ta gọi X[i] thuộc xâu nhị phân N phần tử (với n là số đồ vật trong cửa
hàng) thể hiện sự lựa chọn của tên trộm khi lựa chọn các đồ vật với X[i]=0
tương ứng với việc không lựa chọn đồ vật thứ i còn X[i] = 1 tương ứng với
việc lựa chọn. Vì đề bài cho số lượng mỗi loại đồ vật chỉ có 1 nên việc lựa
chọn có hoặc không lấy đồ vật đó tương ứng với việc chọn lựa bit 0 hay bit 1
trong xâu nhị phân tương ứng với thứ tự các sản phẩm.
Gọi a[i], c[i] là khối lượng và giá trị của từng sản phẩm; B là khối
lượng tối đa mà túi có thể mang theo; D là khối lượng thực của túi khi đó:
- Giá trị của túi sau khi lấy các đồ vật là:
N


D =  X = ( x1 , x 2 ,.., x N ) : ∑ a j x j ≤ B; x j = 0,1.
j =1



- Khối lượng của túi sau khi lấy các đồ vật là:
N

F ( x1 , x2 ,.., x N ) = ∑ c j x j
j =1

Bài toán được đơn giản hóa trở thành:
Cho ai, ci , B, N (i =1, 2,..,N) là những số nguyên dương và tập hợp
N



D =  X = ( x1 , x 2 ,.., x N ) : ∑ a j x j ≤ B; x j = 0,1.
j =1



Hãy viết chương trình tìm phương án tối ưu XOPT =(x1,x2,..,xN) và giá trị tối
ưu FOPT=F(XOPT) của hàm mục tiêu:
Học viên: Ngô Ngọc Thắng – Lớp HTTT 2016 - 1

19


Tiểu luận Thuật toán nâng cao

GV hướng dẫn : TS Nguyễn Duy Phương

N

F ( x1 , x2 ,.., x N ) = ∑ c j x j → max ; với X = ( x1 , x 2 ,.., x N ) ∈ D .
j =1

Như vậy, mấu chốt để giải quyết bài toán trở thành việc tìm xâu nhị
phân X[i] với i ={1,2….N} thỏa mãn điều kiện của bài toán. Như vậy thuật
toán chúng ta sử dụng giải quyết bài toán sẽ là liệt kê các xâu nhị phân X[i] và
kiểm tra tìm ra xâu nhị phân thỏa mãn yêu cầu bài toán. Kết quả trả về của
chương trình sẽ là giá trị tối đa mà chiếc túi có thể chứa được và phương án
lựa chọn các đồ vật tương ứng.
Thuật toán:
Input: Tệp data.in gồm:
- Hàng đầu tiên là N (số loại đồ vật trong cửa hàng), B (trọng lượng tối

đa của túi);
- Hàng 2 và hàng 3 lần lượt là giá trị và khối lượng của từng loại đồ vật.

Output: Tệp ketqua.out gồm:
- Hàng 1 ghi lại giá trị lớn nhất;
- Hàng 2 ghi lại phương án lựa chọn đồ vật.

Input:int A[max],C[max],X[max],n,B,XOPT[max],FOPT=-3000,ok=1;
void Update(void){//Xử lý dữ liệu
int F=0,D=0;
for each i=1:n do{
F=F+C[i]*X[i]; //Giá trị đồ vật trong túi
D=D+A[i]*X[i];} //Khối lượng đồ vật trong túi
if(F>FOPT&&D<=B){/*Nếu Giá trị đồ vật lớn hơn FOPT, khối
lượng đồi vật trong túi <=B thì gán FOPT = F; XOPT[j]=X[j]*/
FOPT=F;
for each j=1:n do
XOPT[j]=X[j];

Học viên: Ngô Ngọc Thắng – Lớp HTTT 2016 - 1

20


Tiểu luận Thuật toán nâng cao

GV hướng dẫn : TS Nguyễn Duy Phương

}}
void Next_bit_string(void){//sinh xâu nhị phân độ dài n

int i;
for(i=n;i>0&&X[i];i--)
X[i]=0;
if(i>0) X[i]=1;
else ok=0;
}
Output:
FOPT
XOPT

5.2.3. Kiểm nghiệm thuật toán
Với n=4, B=10 với
i

=

1

2

3

4

C[i]=

6

5


3

7

A[i]=

5

4

6

5

Ta có bảng sau:
X[]
F
D
D<=B?
0000
0
0
Yes
0001
7
5
Yes
0010
3
6

Yes
0011
10
11
No
0100
5
4
Yes
0101
12
9
Yes
0110
8
10
Yes
0111
15
15
No
1000
6
5
Yes
1001
13
10
Yes
1010

9
11
No
1011
16
16
No
1100
11
9
Yes
1101
18
14
No
1110
14
15
No
1111
21
20
No
Ta thấy: Trong tất cả các phương án thảo mãn D<=B thì F có giá trị lớn
nhất là 13 tương ứng với phương án X[]= {1, 0, 0, 1}. Như vậy kết quả của
bài toán là: FOPT = 13; XOPT[]={1, 0, 0, 1}.
5.2.4. Mã hóa thuật toán
Học viên: Ngô Ngọc Thắng – Lớp HTTT 2016 - 1

21



Tiểu luận Thuật toán nâng cao

GV hướng dẫn : TS Nguyễn Duy Phương

#include <conio.h>
#include <stdio.h>
#define max 100
int A[max],C[max],X[max],n,B,XOPT[max],FOPT=-3000,ok=1;
FILE *fp;
void Read_data(void){
fp=fopen("data.in","r");
fscanf(fp,"%d%d",&n,&B);
for(int i=1;i<=n;i++)
fscanf(fp,"%d",&C[i]);
for(int i=1;i<=n;i++){
fscanf(fp,"%d",&A[i]);
X[i]=0;}
fclose(fp);
}
void Result(void){
fprintf(fp,"%d\n",FOPT);
for(int i=1;i<=n;i++)
fprintf(fp,"%4d",XOPT[i]);
}
void Update(void){
int F=0,D=0;
for(int i=1;i<=n;i++){
F=F+C[i]*X[i];

D=D+A[i]*X[i];}
if(F>FOPT&&D<=B){
FOPT=F;
for(int j=1;j<=n;j++)
XOPT[j]=X[j];
}
}
void Next_bit_string(void){
int i;
for(i=n;i>0&&X[i];i--)
X[i]=0;
if(i>0) X[i]=1;
else ok=0;
}
int main(void){
Read_data();
fp=fopen("ketqua.out","w");
while(ok){Update();Next_bit_string();}
Result();
fclose(fp);
}

5.2.5. Kết quả thực hiện chương trình
Học viên: Ngô Ngọc Thắng – Lớp HTTT 2016 - 1

22


Tiểu luận Thuật toán nâng cao


GV hướng dẫn : TS Nguyễn Duy Phương

VI. THUẬT TOÁN QUAY LUI
6.1 Khái lược về thuật toán quay lui
Giả sử ta cần xác định bộ X =(x1, x2,..,xn) thỏa mãn một số ràng buộc
nào đó. Ứng với mỗi thành phần xi ta có ni khả năng cần lựa chọn. Ứng với
mỗi khả năng jni dành cho thành phần xi ta cần thực hiện:
• Kiểm tra xem khả năng j có được chấp thuận cho thành phần xi hay
không? Nếu khả năng j được chấp thuận thì nếu i là thành phần cuối cùng
(i=n) ta ghi nhận nghiệm của bài toán. Nếu i chưa phải cuối cùng ta xác định
thành phần thứ i +1.
• Nếu không có khả năng j nào được chấp thuận cho thành phần xi thì
ta quay lại bước trước đó (i-1) để thử lại các khả năng khác.
Thuật toán Back-Track ( int i ) {
For ( j =<Khả năng 1>; j <=ni; j++ ){
if (<chấp thuận khả năng j>) {
X[i] = <khả năng j>;
if ( i ==n) Result();
else Back-Track(i+1);
}}
6.2. Giải quyết bài toán cụ thể bằng thuật toán quay lui
Vẫn với bài tập về chiếc túi nêu trên, ngoài cách tạo ra xâu nhị phân X[]
bằng phương pháp sinh. Chúng ra cũng có thể tạo ra xâu nhị phân X[] để tìm
ra phương án tối ưu XOPT[] bằng phương pháp quay lui.
Chúng ta thay thế thủ tục sinh xâu nhị phân bằng phương pháp quay lui,
thuật toán như sau:
Input: int A[max],C[max],X[max],n,B,XOPT[max],FOPT=-3000;
void Update(void){//Xử lý dữ liệu
int F=0,D=0;


Học viên: Ngô Ngọc Thắng – Lớp HTTT 2016 - 1

23


Tiểu luận Thuật toán nâng cao

GV hướng dẫn : TS Nguyễn Duy Phương

for each i=1:n do{
F=F+C[i]*X[i]; //Giá trị đồ vật trong túi
D=D+A[i]*X[i];} //Khối lượng đồ vật trong túi
if(F>FOPT&&D<=B){/*Nếu Giá trị đồ vật lớn hơn FOPT, khối
lượng đồi vật trong túi <=B thì gán FOPT = F; XOPT[j]=X[j]*/
FOPT=F;
for each j=1:n do
XOPT[j]=X[j];
}
}
void Back_track(int i){//Thủ tục sinh sâu nhị phân độ dài n
for(int j=0;j<=1;i++){
X[i]=j;
if(i=n) Update();
else Back_track(i+1);
}
}
Output:
FOPT
XOPT


Phương pháp kiểm nghiệp thuật toán tương tự như đối với phương pháp
sinh xâu nhị phân. Mã hóa thuật toán thay đổi như sau:
#include <conio.h>
#include <stdio.h>
#define max 100
int A[max],C[max],X[max],n,B,XOPT[max],FOPT=-3000;
FILE *fp;
void Read_data(void){
fp=fopen("data.in","r");
fscanf(fp,"%d%d",&n,&B);
for(int i=1;i<=n;i++)
fscanf(fp,"%d",&C[i]);
for(int i=1;i<=n;i++)
fscanf(fp,"%d",&A[i]);
fclose(fp);
}
void Result(void){
fprintf(fp,"%d\n",FOPT);
for(int i=1;i<=n;i++)
fprintf(fp,"%4d",XOPT[i]);
}
void Update(void){
int F=0,D=0;
for(int i=1;i<=n;i++){
F=F+C[i]*X[i];
D=D+A[i]*X[i];}
if(F>FOPT&&D<=B){

Học viên: Ngô Ngọc Thắng – Lớp HTTT 2016 - 1


24


Tiểu luận Thuật toán nâng cao

GV hướng dẫn : TS Nguyễn Duy Phương

FOPT=F;
for(int j=1;j<=n;j++)
XOPT[j]=X[j];
}
}
void Back_track(int i){
for(int j=0;j<=1;j++){
X[i]=j;
if(i==n) Update();
else Back_track(i+1);
}}
int main(void){
Read_data();
fp=fopen("ketqua.out","w");
Back_track(1);
Result();
fclose(fp);
}

VII. PHƯƠNG PHÁP THAM LAM
7.1. Khái lược về phương pháp tham lam
Thuật toán tham lam (Greedy Algorithm): là thuật toán xem xét quá
trình giải quyết bài toán bằng việc tạo nên lựa chọn tối ưu cục bộ tại mỗi bước

thực hiện với mong muốn tìm ra lựa chọn tối ưu toàn cục. Giải thuật tham lam
thường không mang tính tổng quát. Tuy vậy, bằng việc xem xét các lựa chọn
tối ưu cục bộ cho ta lời giải gần đúng với phương án tối ưu toàn cục cũng là
giải pháp tốt trong thời gian chấp nhận được.
Thuật toán Greedy bao gồm 5 thành phần chính:
1. Một tập các ứng viên mà giải pháp thực hiện tạo ra.
2. Một hàm lựa chọn (selection fuction) để chọn ứng viên tốt nhất cho
giải pháp.
3. Một hàm thực thi (feasibility function) được sử dụng để xác định các
ứng viên có thể có đóng góp cho giải pháp thực thi.
4. Một hàm mục tiêu (objective function) dùng để xác định giá trị của
phương pháp hoặc một phần của giải pháp.
5. Một hàm giải pháp (solution function) dùng để xác định khi nào giải
pháp kết thúc.
Học viên: Ngô Ngọc Thắng – Lớp HTTT 2016 - 1

25


×