Trong cuộc sống có nhiều vấn đề buộc ta phải lựa chọn hoặc tìm ra những phương án để giải quyết được vấn đề. Trong toán học cũng thế, để giải một bài toán đòi hỏi ta phải chọn được phương án giải quyết bài toán một cách tối ưu để thu được kết quả mong muốn. Vấn đề đặt ra ở đây là, trong Tin học với mỗi bài toán thường có rất nhiều phương pháp giải. Nhưng để tìm được phương pháp giải tối ưu không phải là vấn đề đơn giản. Giả sử đặt vào bài toán tìm trường hợp tối ưu trong các liệt kê tổ hợp luôn là một trong những bài toán được quan tâm hàng đầu hiện nay. Một bài toán liệt kê tổ hợp cần được đảm bảo không được bỏ sót cũng như trùng lặp bất kỳ một trường hợp nào. Phương pháp quay lui (backtracking) là một trong những phương pháp liệt kê tổ hợp hữu hiệu và mang tính phổ dụng cao. Tuy là thuật toán quay lui là không mới, không tối ưu trong việc giải quyết một số bài toán, nhưng cũng có nhiều bài toán ứng dụng như hoán vị, tập con, tìm đường đi hay mê cung thì cũng chỉ có thuật toán quay lui là lựa chọn phù hợp, nó giúp giải quyết vấn đề một cách đơn giản và dễ dàng hơn. Vì vậy mà “thuật toán quay lui và ứng dụng” được lựa chọn làm đề tài. Ý tưởng cơ bản của “thuật toán quay lui” là liệt kê hết tất cả các khả năng có thể, hay còn gọi là phương pháp vét cạn, duyệt hết cấu hình
Trang 1ĐẠI HỌC ĐÀ NẴNG TRƯỜNG ĐẠI HỌC SƯ PHẠM
KHOA TIN HỌC
ĐỒ ÁN THUẬT TOÁN
ĐỀ TÀI
THUẬT TOÁN QUAY LUI
VÀ ỨNG DỤNG
ĐỂ TÌM ĐƯỜNG ĐI TRONG MÊ CUNG
Đà Nẵng – 2018
Trang 2Mục lục
Trang 3MỞ ĐẦU
1 Lí do chọn đề tài
Trong cuộc sống có nhiều vấn đề buộc ta phải lựa chọn hoặc tìm ra những phương án để giải quyết được vấn đề Trong toán học cũng thế, để giải một bài toán đòi hỏi ta phải chọn được phương án giải quyết bài toán một cách tối ưu để thu được kết quả mong muốn Vấn đề đặt ra ở đây là, trong Tin học với mỗi bài toán thường có rất nhiều phương pháp giải Nhưng để tìm được phương pháp giải tối ưu không phải là vấn đề đơn giản Giả sử đặt vào bài toán tìm trường hợp tối ưu trong các liệt kê tổ hợp luôn là một trong những bài toán được quan tâm hàng đầu hiện nay Một bài toán liệt kê tổ hợp cần được đảm bảo không được bỏ sót cũng như trùng lặp bất kỳ một trường hợp nào Phương pháp quay lui (backtracking) là một trong những phương pháp liệt kê tổ hợp hữu hiệu và mang tính phổ dụng cao Tuy là thuật toán quay lui là không mới, không tối ưu trong việc giải quyết một số
bài toán, nhưng cũng có nhiều bài toán ứng dụng như hoán vị, tập con, tìm đường
đi hay mê cung thì cũng chỉ có thuật toán quay lui là lựa chọn phù hợp, nó giúp
giải quyết vấn đề một cách đơn giản và dễ dàng hơn Vì vậy mà “thuật toán quay lui và ứng dụng” được lựa chọn làm đề tài
Ý tưởng cơ bản của “thuật toán quay lui” là liệt kê hết tất cả các khả năng có thể, hay còn gọi là phương pháp vét cạn, duyệt hết cấu hình
2 Mục tiêu và nhiệm vụ
- Nghiên cứu tổng quan về thuật toán quay lui (lịch sử ra đời, cơ sở lý thuyết, khái
niệm, những ưu điểm, hạn chế của thuật toán)
- Ứng dụng của thuật toán quay lui vào các bài toán điển hình
3 Cấu trúc báo cáo
Chương 1: Cơ sở lý thuyết về thuật toán quay lui
Chương 2: Phát biểu vấn đề
Trang 4Chương 3: Kết quả và ứng dụng
CHƯƠNG 1: CƠ SỞ LÝ THUYẾT 1.1 Lịch sử ra đời
- “Backtrack” (quay lui) được giới thiệu lần đầu tiên bởi nhà toán học người Mỹ Dr.D.H Lehmer vào năm 1950
- R.J Walker là người đầu tiên mô phỏng thuật toán này vào năm 1960
- Sau đó, thuật toán quay lui được S Golamb và L Baumert phát triển
1.2 Tổng quan về thuật toán quay lui
1.2.1 Khái niệm
- Quay lui (backtracking) là phương pháp thử các khả năng của bài toán cho đến
khi tìm thấy lời giải đúng Phương pháp quay lui về bản chất là quá trình tìm kiếm
theo chiều sâu (Depth first search – DFS) trên đồ thị không gian trạng thái của bài
toán
1.2.2 Dấu hiệu nhận biết bài toán có thể sử dụng thuật toán quay lui
- Một bài toán liệt kê tổ hợp luôn cần phải đảm bảo hai nguyên tắc, đó là: không
được bỏ sót một cấu hình và không được trùng lặp một cấu hình Có thể nói rằng phương pháp liệt kê là cách cuối cùng để có thể giải được một số bài toán tổ hợp hiện nay Một trong những phương pháp liệt kê có tính phổ dụng cap đó là thuật toán quay lui
- Một số bài toán liệt kê đơn giản:
Liệt kê các tổ hợp của hoán vị
Liệt kê các số tự nhiên chia hết cho 5
Liệt kê các đường đi để thoát khỏi mê cung
Trang 51.2.3 Ý tưởng của thuật toán
Tại mỗi bước, nếu có lựa chọn được chấp nhận thì ghi nhận lại lựa chọn này
và tiến hành thử các bước thử tiếp theo Còn ngược lại không có lữa chọn nào thích hợp thì làm lại bước trước, xóa bỏ ghi nhận và quay về chu trình thử các lựa chọn còn lại
Hành động này được gọi là quay lui, thuật toán thể hiện phương pháp này gọi
là quay lui
Điểm quan trọng của thuật toán là phải ghi nhỡ mỗi bước đi qua để tránh trùng lặp khi quay lui Dễ thấy là các thông tin này được lưu trữ vào một ngăn xếp, nên thuật toán thể hiện ý thiết kế một cách đệ quy
1.2.4 Các bước thiết kế thuật toán quay lui
Để xây dựng thuật toán quay lui, chúng ta cần phải phân tích bài toán để thiết kế bốn bước cơ bản dưới đây:
1/ Chọn cách biểu diễn giải pháp
2/ Xây dựng các tập rẽ nhánh A1, A2…An vàxếp thứ tự các phần tử của chúng sao cho dễ xử lý
3/ Xây dựng các điều kiện từ ràng buộc của bài toán để xác định một giải pháp từng phần có là triển vọng không, được gọi là điều kiện tiếp tục
4/ Chọn tiêu chí để xác định một giải pháp từng phần có là giải pháp cuối cùng không
1.2.5 Mô tả
Lời giải của bài toán thường biểu diễn một vec tơ gồm n phần tử A= (A1…An) Phải thỏa mãn các điều kiện nào đó Để chỉ ra lời giải A, ta phải xây dựng dần các thành phần lời giải Ai
Tại bước i:
- Đã xây dựng xong các thành phần A1,….Ai-1
Trang 6- Xây dựng thành phần Ai bằng cách lần lượt thử các khả năng mà Ai có thể chọn
Nếu một khả năng j nào đó phù hợp cho Ai thì ta xác định Ai theo khả năng j Thường phải thêm các thao tác ghi nhận trạng thái của bài toán để hỗ trợ cho bước quay lui Nếu i=n thì ta có được một lời giải, ngược lại thì tiến hành bước i+1 để xác định xi+1
Nếu không có một khả năng nào chấp nhận được cho xi thì ta lùi lại bước trước (bước i-1) để xác định lại thành phần Ai-1
Để đơn giản, ta giả định các khả năng lựa chọn cho các Ai tại mỗi bước như nhau, dó đó ta phải có thêm một thao tác kiểm tra khả năng j nào là chấp nhận cho
Ai
Ta có thể trình bày quá trình tìm kiếm lời giải qua thuật toán quay lui bằng cây sau:
Giải thuật tổng quát
Tìm nghiệm bằng thuật toán quay lui có thể chuyển về tìm kiếm trên cây khôn gian trạng thái, với cây được xây dựng từng mức như sau:
- Các nút con của gốc (thuộc mức 1) là các khả năng có thể chọn cho x1
Trang 7- Giả sử Ai-1 là một nút ở mức thứ i-1, khi đó các nút con của Ai-1 là các khả năng
mà Ai có thể chọn, một khi đã tìm được các thành phần A1…Ai-1
Như vậy, mỗi nút Ai của cây biểu diễn một lời giải bộ phận, đó là các nút nằm trên đường đi từ gốc đến nút
Ta có thể nói việc tìm kiếm nghiệm bằng thuật toán quay lui chính là tìm kiếm theo chiều sâu trên cây không gian các trạng thái
1.2.6 Cấu trúc chung của thuật toán quay lui
Input: Các tập hợp A 1 , A 2 , …., A n
Output: Các tập con S của A 1 x A 2 x … x A n thỏa mãn ràng buộc của bài toán
Sử dụng đệ quy
algorithm Quaylui_Dequy (k)
begin
if ( S= ( s1, s2, …, sk-1 ) là giải pháp ) then xuly (s)
else
// Thử tất cả các giá trị có thể
for j 1 to |Ak| do
Sk akj
// A k = { a k1 , a k2 , }
if ( ( s1, s2, …, sk là có triển vọng ) then
Quaylui_Dequy (k+1) // Lựa chọn tiếp theo
endif
endof
endif
end
Sử dụng vòng lặp
algorithm Quaylui_Vonglap (k)
Trang 8k 1, ik 0
while ( k > 0 ) do
ik ik + 1
v false
while ( v = false and ik ≤ |Ak| ) do
sk = ak
ik
if ( ( s1, …, sk ) là có triển vọng ) then
v true
else ik ik+1
endif
endwhile //Thử giá trị tiếp theo
if ( v = true ) then
if ( S = ( s1, …., sk ) là giải pháp cuối cùng ) then xuly(s)
else
k k+1
//Xây dựng thành phần tiếp theo
ik 0
endif
else k K-1
endif
//Quay lui lại thành phần trước đó
endwhile
end
Trang 10CHƯƠNG 2 : PHÁT BIỂU VẤN ĐỀ 2.1 Đề bài
- Giả sử một mê cung được định nghĩa là một lưới có n x n ô Tìm đường đi trong
mê cung xuất phát từ ô (1,1) đến ô (n,n) sao cho mỗi ô chỉ qua đúng một lần Biết rằng, chỉ cho phép di chuyển lên, xuống, trái và phải nếu như vị trí tiếp theo là trống và nằm trong mê cung
2.2 Phân tích
Chúng ta có thể phân tích bài toán mê cung như sau:
- Dùng ma trận M [ 1 n, 1 n ] để lưu trữ mê cung sao cho: M [ i,j ] = 0 nếu ô (i,j)
là rỗng, và ngược lại M [ i,j ] = 1 nếu ô (i,j) có chướng ngại vật
- Tìm tất cả các đường đi S = ( s1, s2, …., sn ), sk trong {1,…., n} x {1,…., n} chỉ ra chỉ số tương ứng ô đi đến các bước k sao cho:
s1 là ô xuất phát ( 1, 1 )
sm là ô đích ( n,n )
M[sk] = 0 ( ô được đi đến phải rỗng )
si ≠ sj với mọi i ≠ j ( mỗi ô chỉ đi qua nhiều nhất một lần )
sk-1 và sk là các ô kề nhau
2.3 Thiết kế thuật toán
Từ những phân tích trên, chúng ta có các bước thiết kế cho thuật toán quay lui như sau:
1/ Biểu diễn giải pháp: Giải pháp được biểu diễn bởi véc – tơ S = (s1, s2, …., sn) với
sk là ô đi đến ở bước k
2/ Xây dựng tập đầu vào A1, A2, …., An và thứ tự các phần tử: Ak = {1, …, n} x {1,
…, n}, với mọi k, các phần tử sẽ được xử lý tăng dần
3/ Điều kiện tiếp tục: Giải pháp từng phần S = (s1, s2, …, sn) phải thoản mãn ràng buộc bài toán là:
Trang 11 M[sk] = 0
si ≠ sj với mọi i ≠ j
sk-1 và sk là các ô kề nhau
4/ Tiêu chí để xác định một giải pháp từng phần có là giải pháp cuối cùng: sk là ô đích ( n,n )
Thuật toán quay lui tìm đường đi trong mê cung
Input: số ô n, ma trận mê cung M [ 1 n, 1 n ]
Output: in tất cả các cách đi
alogrithm MeCung (k)
begin
if ( s [k-1] = ( n,n ) ) then print ( s [1 k-1] )
else
//Đi lên
s [k] i s [k-1] i-1, s [k] j s [k-1] j
if (TrienVong ( s [1…k] ) ) then MeCung ( k+1 ) endif
// Đi xuống
s [k] i s [k-1] i+1, s [k] j s [k-1] j
if (TrienVong ( s [1…k] ) ) then MeCung ( k+1 ) endif
//Qua trái
s [k] i s [k-1] i, s [k] j s [k-1] j - 1
if (TrienVong ( s [1…k] ) ) then MeCung ( k+1 ) endif
// Qua phải
s [k] i s [k-1] i-1, s [k] j s [k-1] j + 1
if (TrienVong ( s [1…k] ) ) then MeCung ( k+1 ) endif
endif
end
Hàm TrienVong
Trang 12TrienVong ( s[1…k] )
begin
if ( s[k] i < 1 or s[k] I > n or s[k] j < 1 or s[k] j > n ) then
return false // Ô ngoài mê cung
endif
if ( M[ s [k] i, s [k] j ] = 1) then return false endif
for q from 1 to k-1 do
if ( s[k] i = s [q] i and s [k] j = s [q] j ) then
return false
endif
endof
return true
end
Trang 13CHƯƠNG 3: KẾT QUẢ VÀ ỨNG DỤNG
[Nêu các ứng dụng, kết quả của thuật toán, chương trình ]
3.1
3.2
KẾT LUẬN
1 Kết luận
- Với vấn đề đặt ra “thuật toán quay lui và ứng dụng để tìm đường đi trong mê
cung”, đồ án đã đạt được một số kết quả:
Tìm hiểu cơ sở lý thuyết của thuật toán quay lui
Ứng dụng để tìm đường đi trong mê cung
Cài đặt chương trình và chjay thử
nghiệm-Đồ án có thể được ứng dụng trong quá trình học tập của sinh viên
2 Hướng nghiên cứu tiếp theo
- Tiếp tục tìm hiểu các bài toán có thể giải quyết được bằng thuật toán quay lui
- Kết hợp thuật toán quay lui với một số kĩ thuật như nhánh cận nhằm đạt hiệu quả tốt nhất
- Xây dựng chương trình ứng dụng có giao diện trực quan, dễ sử dụng hơn
Trang 14TÀI LIỆU THAM KHẢO
1 https://ndtls.com/de-tai-ung-dung-thuat-toan-quay-lui-vao-giai-bai-toan-liet-ke/
2.https://vi.wikipedia.org/wiki/Quay_lui_(khoa_h%E1%BB%8Dc_m
%C3%A1y_t%C3%ADnh)
3 http://www.giaithuatlaptrinh.com/?p=58
4 https://viblo.asia/p/thuat-toan-quay-lui-backtracking-bJzKmLbD59N
5 http://vietjack.com/cau-truc-du-lieu-va-giai-thuat/thuat-toan-quay-lui.jsp
6 http://simplecodecjava.blogspot.com/2015/10/thuat-toan-thuat-toan-quay-lui-back.html
7 http://v1study.com/giai-thuat-va-lap-trinh-thuat-toan-quay-lui.html
Trang 15PHỤ LỤC
Chương trình minh họa 1.
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#define nmax 1000
FILE *fpin, *fpout;
int s_i[nmax], s_j[nmax];
int di[4]={-1, 0, 0, 1};
int dj[4]={0, -1, 1, 0};
int M[nmax][nmax];
int n;
void doc_du_lieu() {
int i,j,l;
/* doc du lieu vao tu tep tin input.txt */
if ((fpin = fopen("input.txt", "r")) == NULL) { printf ("khong the mo tep du lieu vao! \n"); exit(1);
}
fscanf (fpin, "%d", &n);
for (i=1; i<=n; i++)
for (j=1; j<=n; j++)
fscanf (fpin, "%d", &M[i][j]);
fclose (fpin);
Trang 16int trienvong (int k) {
int q;
if (s_i[k]<1 || s_i[k]>n || s_j[k]<1 || s_j[k]>n) return 0;
if (M[s_i[k]][s_j[k]] == 1) return 0;
for (q=1; q<k; q++)
if (s_i[k] == s_i[q] && s_j[k] == s_j[q]) return 0;
return 1;
}
void mecung(int k) {
int q;
if (s_i[k-1] == n && s_j[k-1] == n) {
for (q=1; q<k; q++)
fprintf (fpout, "(%d %d)", s_i[q], s_j[q]); fprintf (fpout, ".\n");
}
else
for (q=0; q<4; q++) {
s_i[k] = s_i[k-1] + di[q];
s_j[k] = s_j[k-1] + dj[q];
if (trienvong (k)) mecung(k+1);
}
}
Trang 17int main() {
doc_du_lieu();
//mo file ghi ket qua output.txt
if ((fpout = fopen("output.txt", "w")) == NULL){
printf ("khOng the mo tep tin ghi ket qua \n");
exit (1);
}
s_i[1] = s_j[1] =1;
mecung(2);
fclose (fpout);
return 0;
}
Giải thích chương trình
- Dữ liệu đầu vào được lưu trong tệp tin input.txt Có cấu trúc như sau: dòng đầu tiên chứa một số nguyên dương n; các dòng tiếp theo là ma trận n x n biểu diễn mê cung (0 là rỗng, 1 là chướng ngại vật)
- Các cách đi được ghi trong tệp tin output.txt, mỗi cách đi thể hiện bằng một
véc-tơ S = ( s1, s2, …., sn) được ghi trên một dòng
- Lệnh FILE *fpin, *fpout dùng để gọi tập tin văn bản vào chương trình
- Lệnh fscanf (fpin, "%d", &n) đọc dữ liệu từ file input.txt
- Lệnh fprintf(fpout, "(%d %d)", s_i[q], s_j[q]) dùng để in vào file output.txt