Tải bản đầy đủ (.docx) (22 trang)

Báo Cáo Cấu trúc dự liệu và giải thuật : Chiến lược quy hoạch động

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 (235.28 KB, 22 trang )

Giảng Viên Hướng Dẫn : Vũ Văn Định

LỜI NÓI ĐẦU

Cấu Trúc Dữ Liệu và Giải Thuật là một trong những môn học cơ bản của
sinh viên ngành Công nghệ thông tin. Các cấu trúc dữ liệu và các giải thuật được
xem như là hai yếu tố quan trọng nhất trong ngành lập trình, đúng như câu nói nổi
tiếng của Niklaus Wirth: Chương trình = Cấu trúc dữ liệu + Giải thuật (Programs =
Data Structues + Algỏithms). Nắm vững các cấu trúc dữ liệu và các giải thuật là cơ
sở để sinh viên tiếp cận với các thiết kế và xây dựng phần mềm cũng như sử dụng
các công cụ lập trình hiện đại. Một trong những phần quan trọng của bộ môn Cấu
Trúc Dữ Liệu và Giải Thuật là phần chiến lược thiết kế thuật toán . Trong những
chiến lược thuật toán hiện nay như chiến lược chia để trị , chiến lược quay lui,
chiến lược quy hoạch động…Thì chiến lược quy hoạch động là chiến lược giúp
chúng ta rút ngắn được thời gian giải của bài toán . Trong bài báo cáo dưới đây sẽ
phân tích về chiến lược quy hoạch động cũng như ứng dụng để tìm hành trình với
chi phí ít nhất trong bài tốn người du lịch.
Để có thể hồn thành tốt báo cáo mơn học em xin chân thành cảm ơn tới các
thầy , các cơ và bạn bè nói chung và thầy ‘ Vũ Văn Định’ nói riêng đã ln giúp đỡ
em trong q trình học tập .
Em xin chân thành cảm ơn!

Sinh Viên Thực Hiện: Nguyễn Quốc Tuấn – 1781310178

1


Giảng Viên Hướng Dẫn : Vũ Văn Định

CHƯƠNG 1: TỔNG QUAN CHIẾN LƯỢC QUY HOẠCH ĐỘNG
TRONG QUÁ TRÌNH XÂY DỰNG THUẬT TỐN.


1.1

Giới thiệu chiến lược quy hoạch động .
-

Thuật tốn quy hoạch động được ưa chuộng bởi vì ban đầu, bài tốn

có mn hình vạn trạng và bạn phải suy nghĩ rất nhiều mới tìm ra được lời
giải. Khơng có một công thức chuẩn mực nào áp dụng được cho mọi bài
tốn. Bởi vì sự phổ biến của nó, bạn bắt buộc phải cực kỳ thuần thục thuật
toán này nếu muốn có kết quả tốt trong các cuộc thi.
-

Quy hoạch động(Dynamic Progarming) là chia bài toán lơn thành các

bài toán nhỏ hơn có các bài tốn con có cấu trúc con tối ưu , tính chất gối
nhau sau đó tổng hợp lời giải của các bài tốn con đó thành lời giải của bài
toán lớn ban đầu.
-

Quy hoạch động được nhà toán học Richard Bellman phát minh năm

1953. Trong ngành Khoa học máy tính (Computer Science), quy hoạch động
là một trong những chiến lược thiết kế thuật tốn vơ cùng quan trọng. Quy
hoạch động là một trong những phương pháp làm giảm thời gian chạy của
các thuật toán thể hiện tính chất của các bài tốn con gối nhau (Overlapping
subproblem) và cấu trúc con tối ưu (Optimal substructure).

1.2 Xây dựng một bài toán quy hoạch động.
Xây dựng bài toán quy hoạch động gồm 3 giai đoạn:


Sinh Viên Thực Hiện: Nguyễn Quốc Tuấn – 1781310178

2


Giảng Viên Hướng Dẫn : Vũ Văn Định

Giai Đoạn 1: Phân tích xem bài tốn cần giải có phân tích thành bài tốn có
các bài tốn con gối nhau , bài tốn có cấu trúc con tối ưu được hay khơng.

Giai Đoạn 2: Nếu các bài tốn con có tính chất :
- Bài tốn có các bài tốn con gối nhau.
- Bài tốn có cấu trúc con tối ưu.
Khi đó giải các bài tốn con cơ sở sau đó lưu trữ các bài tốn con đó để sử
dụng nhiều lần trong q trình lặp của bài tốn.
Giai Đoạn 3: Gộp các lời giải của bài toán con thành bài toán lớn hơn, tiếp
tục cho đến khi gặp bài toán yêu cầu.
Bài toán cơ sở là bài toán hiển nhiên đúng hoặc dễ dàng giải được. Xây
dựng công thức truy hồi để giải bài toán lớn hơn. Từ lời giải của bài tốn cơ
sở với cơng thức truy hồi ta có thể sử dụng mảng một chiều hoặc mảng nhiều
chiều để lưu trữ kết quả của bài toán cơ sở . Dựa vào phương án và công
thức truy hồi để giải bài toán ban đầu.

1.3

Ưu nhực điểm của chiến lược quy hoạch động.
- Ưu điểm: Các lời giải của bài toán con lưu trữ theo phương án , khi cần chỉ
tổng hợp và gọi lời giải, giảm thời gian chạy của thuật toán.
- Nhược điểm: Tốn vùng nhớ để lưu trữ lời giải các bài toán con.


Sinh Viên Thực Hiện: Nguyễn Quốc Tuấn – 1781310178

3


Giảng Viên Hướng Dẫn : Vũ Văn Định

1.4

So sánh chiến lược quy hoạch động với chiến lược chia để trị.
Giống nhau :
- Hai chiến lược chia để trị và chiến lược quy hoạch động đều
chia các bài toán lớn thành các bài toán con nhỏ hơn , dễ giải
quyết hơn để tổng hợp lời giải , giải quyết những bài toán
lớn .
Khác nhau :
- Chiến lược chia để trị : Từ bài toán lớn chia thành các bài
toán con nhỏ hơn sau đó tổng hợp lại được kết quả của bài
toán ban đầu. (Top down)
- Chiến lược quy hoạch động : Từ bài toán cơ sở giải các bài
toán lớn hơn tiếp tục như thế cho đến khi gặp bài toán đề ra.
(Bottom up)

1.5

Ứng dụng của chiến lược quy hoạch động.

Sinh Viên Thực Hiện: Nguyễn Quốc Tuấn – 1781310178


4


Giảng Viên Hướng Dẫn : Vũ Văn Định

Quy hoạch động có ứng dụng rất lớn trong việc giảm thời gian chạy của một
số thuật toán như chiến lược quay lui ,… và khử đệ quy.

Sinh Viên Thực Hiện: Nguyễn Quốc Tuấn – 1781310178

5


Giảng Viên Hướng Dẫn : Vũ Văn Định

CHƯƠNG 2: ỨNG DỤNG CHIẾN LƯỢC QUY HOẠCH ĐỘNG ĐỂ
TÌM HÀNH TRÌNH VỚI CHI PHÍ ÍT NHẤT TRONG BÀI TỐN
NGƯỜI DU LỊCH.
2.1

Giới thiệu chung về bài toán người du lịch . (Traveling Salesman
Problem - TSP)
Vấn đề của bài toán người du lịch (TSP): Có n thành phố đánh số
từ 1 đến n và các tuyến đường giao thông hai chiều giữa chúng, mạng lưới
giao thông này được cho bởi mảng C[1..n,1..n], ở đây C[i,j] là chi phí đi
đoạn đường trực tiếp từ thành phố i đến thành phố j. Một người du lịch xuất
phát từ thành phố 1, muốn đi thăm tất cả các thành phố còn lại mỗi thành phố
đúng 1 lần và cuối cùng quay lại thành phố 1. Hãy tìm hành trình với chi phí
ít nhất cho người đó.


2.2

Giải quyết bài toán người du lịch (TSP).
Bài toán người du lịch có thể giải bằng phương pháp quay lui với thời
gian O(n!). Vậy nên phương pháp giaiả bằng quy hoạch động sẽ đáp ứng
được thời giai giải bài toán với O(n22n).
Giả sử T[1,2,…], T[1]=1, là một hành trình tối ưu đi từ thành
phố 1 đến thành phố T[n] qua n−1 thành phố khác và quay lại 1. Vì hành
trình T[1,2,…,n] là tối ưu, hành trình T[2,3,…,n−1] đi từ 1 tới T[n] qua các
thành phố {2,3,…,n}∖{T[n]} cũng phải là hành trình tối ưu. Như vậy, nếu
gọi Cost(S,i) là chi phí nhỏ nhất đi từ 1 tới i qua các thành phố trong tập
hợp S (khơng chứa i), ta có cơng thức đệ quy sau:

Sinh Viên Thực Hiện: Nguyễn Quốc Tuấn – 1781310178

6


Giảng Viên Hướng Dẫn : Vũ Văn Định

Cost(S,i) = minj ∈ S{Cost(S∖{j},j) + C[i,j]}

Do đường đi với chi phí nhỏ nhất đi từ 1 tới i chỉ qua S, đường đi này
phải đi qua một đỉnh j nào đó thuộc S trước khi đến i. Tức là đường đi có
dạng 1→…→j→i. Do đó Cost(S,i)=Cost(S∖j,j)+C[j,i]. Tuy nhiên ở đây ta
khơng biết cụ thể j mà chỉ biết j thuộc S. Do đó, ta phải duyệt qua tất cả
các j có thể thuộc S và lấy min.
Để tính Cost(S,i), ta chỉ cần đảm bảo các giá trị C(S′,j) với S′=S∖{j} đã
được tính trước. Giả mã như sau:


DYNAMICTSP(C[1,2,…,n,1,2,…,n]):
Cost[1,1]=+∞
for s←1 to n−1 do
for each S← a subset of {1,2,…,n} of size s
Cost[S,1]+∞
for each i←S∖{1}
SUBTOURCOST(S∖{i},i)
opt ←+∞
for j←2 to n
opt ←min{opt,Cost[{2,3,…,n}∖{j},j]+C[i,j]}
return opt

Sinh Viên Thực Hiện: Nguyễn Quốc Tuấn – 1781310178

7


Giảng Viên Hướng Dẫn : Vũ Văn Định

Hàm SubtourCost(S,j) tính chi phí của hành trình từ 1 đến j qua mỗi thành
phố trong S (khơng chứa j) chính xác 1 lần và được tính theo cơng thức đệ
quy ở trên. Giả mã như sau:

SUBTOURCOST(S,i):
Cost[S,i]←+∞
for each j←S
Cost[S,i]←min{Cost[S,i], Cost[S∖{j},j]+C[i,j]}

Thời gian tính của thuật tốn trên có thể được quy về thời gian cập nhật
bảng Cost[S⊆{2,3,…,n}][1,2,…,n] có kíc thước O(n2n). Mỗi phần từ của

bảng được cập nhật theo thủ tục SubtourCost(S,j) và mất thời gian O(|
S|)=O(n). Do đó tổng thời gian tính tốn của thuật toán là O(n22n).
+
Để việc truy xuất bảng Cost [S⊆{2,3,…,n}][1,2,…,n] được hiệu quả,
chúng ta có thể mã hóa bằng cách gán cho mỗi tập con S của {1,2,…,n} một
số duy nhất, gọi là id, để việc truy xuất hay cập nhật C[S,i] được hiệu qủa. Ta
có thể mã hóa một tập con S của tập n phần tử bằng một số
ngun n bít a1a2….an,
trong
đó ai=1 nếu
phần
tử i∈S và ai=0
nếu i∉S. id của S chính là giá trị của số ngun đó.
Ví dụ: n=5, tập con S=1,2,4 có thể được biểu diễn bởi số ngun ở hình dưới
đây, và có id = 010112 = 11.

Hình 2.2.1

+
Phương pháp sinh các tập con của {1,2,…,n}, Bây giờ ta giải quyết bài
toán sinh các tập con kích thước k của tập hợp có kích thước n. Ở đây ta sẽ

Sinh Viên Thực Hiện: Nguyễn Quốc Tuấn – 1781310178

8


Giảng Viên Hướng Dẫn : Vũ Văn Định

sinh tập con theo thứ tự từ điển. Như vậy ta có thể quy về bài tốn cho một

tập con S, tìm tập con tiếp theo có k phần tử theo thứ tự từ điển. Giả sử tập
con S đó được cho dưới dạng mã hóa bit như trên, ta quy về bài tốn:
Problem: Cho một số ngun n-bít v có k bít 1. Tìm số ngun n-bít w tiếp
sau v theo thứ tự từ điển và có k bít 1.
Ví dụ: n=5, k=3, v=010112 , số nguyên tiếp theo sẽ là w=011012 . Ta có thể
nhận thấy là nếu v có a (a có thể bằng 0) bít cuối cùng là 0, trước đó là b bít
1 (Hình 2.2.2), thì w sẽ có b−1 bít cuối là 1, trước đó là a+1 bít 0 và
trước a+1 bít 0 đó là 1 bít 1 (Hình 2.2.3).

Hình 2.2.2

Hình 2.2.3

Như vậy để giải bài tốn trên, ta tìm cách chuyển v thành w giống như trong
hình trên. Giả mã như sau:

int NEXTSET(unsigned int v, int n):
unsgined int w
unsgined int t←(v|(v−1))+1
w←t|((((t&−t)/(v&−v))>>1)−1)
if w>>n=0
return w
else
return 0
Các thao tác trong hàm NextSet(unsigned int v, int n) được minh hoạ bởi
hình sau: (Hình 2.2.4)

Sinh Viên Thực Hiện: Nguyễn Quốc Tuấn – 1781310178

9



Giảng Viên Hướng Dẫn : Vũ Văn Định

Hình 2.2.4

+

Bài tốn tiếp theo chúng ta phải giải đó là liệt kê các tập con:

Problem: Cho một tập S⊆{1,2,…,n} với kích thước |S|, liệt kê các tập con
của S có kích thước |S|−1|.
Với mỗi tập S, chúng ta dùng danh sách liên kết để lưu các tập con
của S có kích thước |S|−1. Đầu của danh sách lưu S. Mỗi mắt xích gồm hai
trường: trường id lưu mã của tập con S′ và trường x lưu phần tử x=S∖S′. Mã
giả được cho ở dưới, seed là id của tập con S.
GENERATESUBSET(int seed, n):
node *head = NEWNODE()
head→→id = seed;
node *iterator = head
for i←0 to n−1

Sinh Viên Thực Hiện: Nguyễn Quốc Tuấn – 1781310178

10


Giảng Viên Hướng Dẫn : Vũ Văn Định

if ((seed>>i)&1) = 1

[[check (i+1)-th bit to be 1]]
node *elem = NEWNODE()
elem→id =(1<[[set (i+1)-th bit of seed to 0]]
elem→x =i+1
iterator→next = elem;
iterator == iterator→next.
return head

Dựa vào thủ tục GenerateSubset(int seed, n), ta có thể tính trước mảng
Subsets[1,2,…,2n−1], trong đó mỗi phần tử của mảng Subsets[i] là
một danh sách liên kết lưu các tập con có kích thước |S|−1 của
tập S có id là i. Mã giả như sau:

GenerateAllSubsets(n):
for i←1 to 2n−1
Subsets[i]←GenerateSubset(i,n)
Như vậy, chúng ta đã giải quyết xong bài tốn mã hóa các tập con và
sinh các tập con của một tập S có kích thước |S|−1. Vấn đề cịn lại chỉ
là ghép các phần lại với nhau để giải quyết bài toán.
Giả mã của thuật toán như sau:

Sinh Viên Thực Hiện: Nguyễn Quốc Tuấn – 1781310178

11


Giảng Viên Hướng Dẫn : Vũ Văn Định

DYNAMICTSPINC(A[1,2,…,n]):

size 11<opt ←+∞
for s←2 to n−1 do
set ←(1<[[first set in the lexicographic order]]
repeat
if (set&1=0)
[[set does not contain 1]]
node *subsetList == Subsets[set]→next [[skip the
head]]
for each elem of subsetList
S= elem→id
i= elem→x
SUBTOURCOST(S, i):
if s=n−1
opt ←min{opt, Cost[S,i]+C[i,1]}
set = NEXTSET(set, n)
until set =0
return opt
Thủ tục SubtourCost(S, i) có mã giả như sau:

SubtourCost(S,i):
node *subsetList = Subsets[S]→next
[[skip the head]]
if susbet→→id =0
[[SS has size 1]]
Cost[S,i]←C[1, subset→x]+C[subset→x,i]
else
Cost[S,i]=+∞
for each elem of subsetList

Cost[S,i] = min{ Cost[S,i], Cost[elem→id, elem→x] + C[elem→x, i]}

Sinh Viên Thực Hiện: Nguyễn Quốc Tuấn – 1781310178

12


Giảng Viên Hướng Dẫn : Vũ Văn Định

CHƯƠNG 3: CÀI ĐẶT THUẬT TỐN
3.1

Cài đặt code
Giả sử các thành phố có đỉnh bắt đầu là 1,2,3,4 (), giữa các thành
phố có trọng số là chi phí khi đi .

Hình 3.1.1 Sơ đồ thành phố ban đầu

Hình 3.1.2 Sơ đồ hành trình tối ưu

#include<time.h>
#include<stdio.h>
#include<string.h>
#define min(x, y) (((x) < (y)) ? (x) : (y))
#define MAX (1<<22)
#define INFTY 100000000
#define NULL 0

int Cost[MAX][30];
int C[30][30];

Sinh Viên Thực Hiện: Nguyễn Quốc Tuấn – 1781310178

13


Giảng Viên Hướng Dẫn : Vũ Văn Định

struct node *generate_set(unsigned int seed, int n);
struct node *generate_subset(unsigned int seed, int n);

// cho thuat toán quay lui
int visit[30];
int backtrackOpt = INFTY;// gia tri toi uu cho thuat toan quay lui

struct node {
int id;
int x;
struct node *next;
};
void main (void){
int n = 4;
C[0][0] = 0; C[0][1] = 5; C[0][2] = 7; C[0][3] = 3;
C[1][0] = 5; C[1][1] = 0; C[1][2] = 10; C[1][3] = 4;
C[2][0] = 7; C[2][1] = 10; C[2][2] = 0; C[2][3] = 2;
C[3][0] = 3; C[3][1] = 4; C[3][2] = 2; C[3][3] = 0;
printf("tour toi uu la : %d \n", dynamic_tsp(n));

}

int dynamic_tsp (int n){


Sinh Viên Thực Hiện: Nguyễn Quốc Tuấn – 1781310178

14


Giảng Viên Hướng Dẫn : Vũ Văn Định

unsigned int set;
int size = 1 << n;
struct node *subsets[size];
generate_all_subsets(subsets,size, n);
struct node *subset;
struct node *iterator;
int i = 0;
int opt_tour = INFTY;
int s = 2;
for(s = 2; s < n; s++){
set = (1<while(1){
if((set&1) == 0){
subset = subsets[set]; // tap hop con cua tap hop "set"
iterator = subset->next; // bo qua head
while(iterator != NULL){
subtour_cost(subsets, iterator->id, iterator->x);
if(s == n-1){// save Cost[S][i] for S such that |S| =
n-2
opt_tour = min(opt_tour,Cost[iterator->id]
[iterator->x] + C[iterator->x-1][0]);
}

iterator = iterator->next;
}
}
Sinh Viên Thực Hiện: Nguyễn Quốc Tuấn – 1781310178

15


Giảng Viên Hướng Dẫn : Vũ Văn Định

set = next_set(set, n);// tao tap con tiep theo
if(set == 0){ // khong co tap con
break;
}
}
}
return opt_tour;
}

// tinh chi phi cua hanh trinh
void subtour_cost(struct node *subsets[], int set_id, int i){
struct node *subset = subsets[set_id];
struct node *iterator = subset->next;
if(iterator->id == 0){// Neu tap hop voi ID set_id chi co 1 phan tu
Cost[set_id][i] = C[0][iterator->x-1] + C[iterator->x-1][i-1];
}else{
Cost[set_id][i] = INFTY;
while(iterator != NULL){
Cost[set_id][i] = min(Cost[set_id][i], Cost[iterator->id][iterator>x] + C[iterator->x-1][i-1]);
iterator = iterator->next;

}
}

Sinh Viên Thực Hiện: Nguyễn Quốc Tuấn – 1781310178

16


Giảng Viên Hướng Dẫn : Vũ Văn Định

}
// liet ke cac tap con
struct node *generate_subset(unsigned int seed, int n){
struct node *head;
head = malloc( sizeof(struct node));
head->next = NULL;
head->id = seed;
struct node *iterator = head;
int i = 0;
for(i = 0; i< n; i++){
if(((seed >> i)&1) == 1){
struct node* elem = malloc( sizeof(struct node) );
elem->id = (1<elem->x = i+1;
elem->next = NULL;
iterator->next = elem;
iterator = iterator->next;
}
}
return head;

}
// tinh kich thuoc mang subsets
void generate_all_subsets(struct node *subsets[], int size, int n){

Sinh Viên Thực Hiện: Nguyễn Quốc Tuấn – 1781310178

17


Giảng Viên Hướng Dẫn : Vũ Văn Định

int i = 0;
for(i = 1; i < size; i++){
subsets[i] = generate_subset(i,n);
}
}

struct node *generate_set(unsigned int seed, int n){
struct node *root;
root = malloc( sizeof(struct node));
root->x = seed;
root->next = NULL;
struct node *iterator = root;
unsigned int w = seed;
while(1){
unsigned set_id = next_set(w,n);
if(set_id != 0){
struct node* elem = malloc( sizeof(struct node) );
elem->x = set_id;
elem->next = NULL;

iterator->next = elem;
iterator = iterator->next;
} else break;
}
return root;

Sinh Viên Thực Hiện: Nguyễn Quốc Tuấn – 1781310178

18


Giảng Viên Hướng Dẫn : Vũ Văn Định

}

int next_set(unsigned int v, int n){
unsigned int w;
unsigned int t = (v |(v - 1)) + 1;
w = t | ((((t & -t) / (v & -v)) >> 1) - 1);
if(w >> n == 0)return w;
else return 0;
}

void backtrack(int i, int currTourVal, int n){
visit[i] = 1;
int j = 0;
int visitable = 0;
for(j = 0; j < n; j++ ){
if(visit[j] == 0){
visitable = 1;

if(currTourVal + C[i][j] < backtrackOpt){
backtrack(j, currTourVal + C[i][j], n);
}
}
}
if(visitable == 0){
backtrackOpt = min(backtrackOpt, currTourVal + C[i][0]);

Sinh Viên Thực Hiện: Nguyễn Quốc Tuấn – 1781310178

19


Giảng Viên Hướng Dẫn : Vũ Văn Định

}
visit[i] = 0;
}

3.2 Hình ảnh kết quả code chạy

Sinh Viên Thực Hiện: Nguyễn Quốc Tuấn – 1781310178

20


Giảng Viên Hướng Dẫn : Vũ Văn Định

KẾT LUẬN
1. Kết quả đạt được

- Sau khi thuật toán được triển khai kết quả nhận được là chi phí tối ưu
cho quá trình tối ưu
-

Sinh Viên Thực Hiện: Nguyễn Quốc Tuấn – 1781310178

21


Giảng Viên Hướng Dẫn : Vũ Văn Định

DANH MỤC THAM KHẢO
-
- Jeff Erickson. Lecture Notes on Single Source Shortest Paths, UIUC,
2014.

-

/>-a/p/quy-hoach-dong-mot-thuat-toan-thanthanh-E375zy01lGW
- />
Sinh Viên Thực Hiện: Nguyễn Quốc Tuấn – 1781310178

22



×