Báo cáo bài tập lớn Môn: Nhập môn trí tuệ nhân tạo Đề bài: áp dụng thuật toán Hill-climbing để giải bài toán N- Queen. Phần I: Giới thiệu về bài toán N – Queen: a. Lịch sử: Cơ sở của bài toán được đưa ra lần đầu tiên vào năm 1848 bởi Mazbezzel với đề bài là xếp 8 quân hậu.Sau đó đã có rất nhiều nhà khoa học nghiên cứu.Năm 1874 nauck tổng quát hóa bài toán thành bài toán xếp N quân. Trong lập trình AI, bài toán xếp n-queen là một trong những bài toán cơ bản, thường được dùng để đánh giá sức mạnh của một thuật toán.Ngoài ra bài toán n-queen cũng được ứng dụng nhiều trong những lĩnh vực khác. b.Đề bài: Cho một bàn cờ kích thước n >=4.Đặt n quân hậu trên bàn cờ sao cho không có quân nào có thể ăn được quân khác.Hay nói một cách khác, không quân hậu nào có thể di chuyển theo quy tắc cờ vua. Mầu của các quân hậu là không có ý nghĩa trong bài toán này. Như vậy lời giải của bài toán là 1 cách xếp n quân hậu sao cho không có 2 quân nào đứng cùng 1 hàng, một cột, hoặc 1 đường chéo. c.Các lời giải cho bài toán: Với n = 8.Bài toán có tất cả 92 lời giải.Trong đó, nếu loại đi các trường hợp đối xứng thì bài toán chỉ còn 12 lời giải: Một trong số những lời giải của bài toán 8 queen:
Trang 1HỌC VIỆN KỸ THUẬT QUÂN SỰ
KHOA CÔNG NGHỆ THÔNG TIN
BÁO CÁO MÔN HỌC TRÍ TUỆ NHÂN TẠO
Giáo viên hướng dẫn: Ngô Hữu Phúc
HÀ NỘI 3/2010
Trang 2Báo cáo bài tập lớn Môn: Nhập môn trí tuệ nhân tạo
Đề bài: áp dụng thuật toán Hill-climbing để giải bài toán N- Queen.
Phần I: Giới thiệu về bài toán N – Queen:
a Lịch sử:
Cơ sở của bài toán được đưa ra lần đầu tiên vào năm 1848 bởi Mazbezzel với đề bài là xếp 8 quân hậu.Sau đó đã có rất nhiều nhà khoa học nghiên cứu.Năm 1874 nauck tổng quát hóa bài toán thành bài toán xếp N quân Trong lập trình AI, bài toán xếp n-queen là một trong những bài toán cơ bản, thường được dùng để đánh giá sức mạnh của một thuật toán.Ngoài ra bài toán n-queen cũng được ứng dụng nhiều trong những lĩnh vực khác
b Đề bài:
Cho một bàn cờ kích thước n >=4.Đặt n quân hậu trên bàn cờ sao cho không
có quân nào có thể ăn được quân khác.Hay nói một cách khác, không quân hậu nào có thể di chuyển theo quy tắc cờ vua Mầu của các quân hậu là không có ý nghĩa trong bài toán này Như vậy lời giải của bài toán là 1 cách xếp n quân hậu sao cho không có 2 quân nào đứng cùng 1 hàng, một cột, hoặc 1 đường chéo
c Các lời giải cho bài toán:
Với n = 8.Bài toán có tất cả 92 lời giải.Trong đó, nếu loại đi các trường hợp đối xứng thì bài toán chỉ còn 12 lời giải:
Một trong số những lời giải của bài toán 8 queen:
Trang 3Cho đến nay vẫn chưa có công thức tính toán chính xác số lượng lời giải cho bài toán tổng quát n-queen
Phần II: Thuật toán Hill-climbing và các thuật toán khác để giải bài toán n-queen:
Có nhiều thuật toán để giải bài toán n – queen Ở đây chỉ nêu và so sánh 4 thuật toán cơ bản là: backtracking, hill-climbing, gene và thuật toán chia để trị
a Thuật toán backtracking:
Ý tưởng của thuật toán duyệt bài toán theo chiều sâu, tức là: đặt từng quân hậu lên bàn cờ sao cho con hậu đang đặt không được tấn công các con hậu trước đó dã có trên bàn cờ.Nếu ở 1 tinh huống mà con hậu thứ k không thể đặt lên bàn cờ (nói cách khác là tất cả các ô trên bàn cờ đều bị k-1 con hậu trước đó tấn công) thì đặt lại con hậu thứ k-1 vào vị trí khác
Giả mã:
Procedure Try_row(i)
For j=1 To n do
If not ok_row(i) And not ok_col(j) And not ok_plus(i+j-1) And not
ok_minus(i-j+n) then
{
solution(i)=j;
ok_col(j)=True;
ok_plus(i+1)=True;
ok_minus(i-j+n)=True;
If i<n then
Trang 4try_row(i+1) ELSE print_solution();
ok_row(i)=False;
ok_col(j)=False;
ok_plus(i+j-1)=False;
ok_minus(i-j+n)=False;
}
Nhận xét: Ưu điểm của thuật toán là dễ cài đặt, có thể đưa ra tất cả lời gải của bài toán Khuyết điểm của giải thuật là thời gian chạy lâu, tón nhiều tài nguyên
hệ thống
b Thuật toán hill-climbing:
Giải thuật leo đồi chính là nền tảng cơ sở của các kỹ thuật tìm kiếm cục bộ Mặc dù đây là giải thuật đơn giản nhưng lại nó lại rất mạnh và hiệu quả trong việc giải quyết các bài toán CSP lớn Thuật ngữ “leo đồi”
(hill-climbing) xuất phát từ cơ chế “tu chỉnh lập”: ở mỗi bước của việc tìm kiếm, chúng ta sẽ chọn một bước chuyển mà nó cải thiện giá trị hàm mục tiêu để thực hiện Giải thuật leo đồi được mô tả như sau:
Procedure Hill_Climbing_Search;
Begin
Khởi tạo danh sách L chỉ chứa trạng thái đầu;
Loop do
If L rỗng then { thông báo thất bại; stop; }
Loại trạng thái u đầu danh sách L;
If u là trạng thái kết thúc then { thông báo thành công; stop; } For mỗi trạng thái v kề u đặt v vào L sao cho các phần tử được
đưa vào đầu danh sách L có đánh giá giảm dần;
End;
Trong giải thuật leo đồi, chỉ những bước chuyển cải thiện được hàm chi phí hoặc không làm cho hàm chi phí thay đổi mới được chọn vì vậy việc tìm kiếm sẽ liên tục bước lên vị trí cao hơn cho đến khi nó gặp điều kiện dừng Khuyết điểm của giải thuật leo đồi:
Vấn đề lớn nhất mà giải thuật leo đồi mắc phải là nó sẽ bị rơi vào vùng tối
ưu cục bộ, đó là lúc chúng ta leo lên một đỉnh mà chúng ta không thể tìm được láng giềng nào tốt hơn được nữa nhưng đỉnh này lại không phải là đỉnh cao nhất
Trang 5Tối ưu cục bộ và tối ưu toàn cục
Khi leo đến một đỉnh tối ưu cục bộ, để tìm được lời giải tốt hơn nữa ta có thể lặp lại thuật toán leo đồi với nhiều điểm xuất phát khác nhau được chọn ngẫu nhiên và lưu lại kết quả tốt nhất ở mỗi lần lặp Nếu số lần lặp đủ lớn thì
ta có thể tìm được đỉnh tối ưu toàn cục, tuy nhiên với những bài toán có không gian tìm kiếm khổng lồ (chẳng hạn như bài toán xếp lịch) ta không thể đưa ra số lần lặp đủ lớn để đảm bảo tìm được lời giải tối ưu Cho nên đây
là phương pháp giải quyết không mang lại nhiều hiệu quả, chúng ta sẽ khảo sát các giải thuật cải tiến khác khắc phục được vấn đề này ở phần sau
Nhận xét: hiệu quả của giải thuật leo đồi phụ thuộc rất nhiều vào “bề mặt” của không gian tìm kiếm bài toán Nếu bài toán chỉ có vài đỉnh tối ưu cục bộ thì giải thuật sẽ tìm ra lời giải tối ưu rất nhanh, tuy nhiên trong trường hợp
bề mặt không gian tìm kiếm lại quá lồi lõm (rất nhiều đỉnh tối ưu cục bộ), giải thuật sẽ bị luẩn quẩn trong vùng tối ưu cục bộ và có thể không tìm ra được lời giải tối ưu của bài toán
Việc khắc phục và áp dụng thuật toán này để giải bài toán n-queen sẽ được trình bày kĩ hơn trong phần III
c Thuật toán gene:
Xếp N hậu lên các vị trí bất kì Ứng với mỗi trạng thái của bàn cờ ta mã hóa chúng thành các gen (hay các mảng 1 chiều).Cho các gen đó lai ghép với nhau để tạo ra các gen mới.Kiểm tra trong số các gen con, xem có gen nào
là trạng thái kết thúc chưa(trạng thái chứa lời giải) nếu có thì dừng lại và đưa
ra lời giải, nếu chưa thì chọn lấy 1 số gene tốt ở cả trong các gene cha mẹ và gene con để tạo ra thế hệ sau.Tiếp tục cho lai ghép đến khi tìm được trạng thái kết thúc
d Thuật toán chia để trị
Trang 6Ý tưởng thuật toán:
Chia bài toán xếp N hậu thành 1 số trường hợp
Ví dụ về 1 cách chia trường hợp:
N từ 4->50 là 1 trường hợp, N từ 50->100 là 1 trường hợp
Tất nhiên đây chỉ là minh họa cho việc chia trường hợp Thực tế thì không thể chia như trên, bởi vì N ở đây có thể tiến tới vô cùng, ta phải có 1 cách chia hợp lý, vửa đảm bảo vét hết tất cả các N, vừa đảm bảo có được lời giải cho từng trường hợp một Cụ thể như sau:
thực hiện qua 7 bước:
1 Chia n cho 12 lấy số dư r (r= 8 với bài toán tám quân hậu).
2 Gán lần lượt các số chẵn từ 2 đến N vào N/2 phần tử đầu tiên của mảng
N phần tử đã tạo từ trước.
3 Nếu số dư r là 3 hoặc 9, chuyển 2 xuống cuối mảng.
4 Bổ sung lần lượt các số lẻ từ 1 đến n vào cuối mảng, nhưng nếu r là 8, đổi chỗ từng cặp nghĩa là được 3, 1, 7, 5, 11, 9, ….
5 Nếu r = 2, đổi chỗ 1 và 3, sau đó chuyển 5 xuống cuối mảng.
6 Nếu r = 3 hoặc 9, chuyển 1 và 3 xuống cuối danh sách.
7 Lấy danh sách trên làm danh sách chỉ số cột, ghép vào danh sách chỉ số dòng theo thứ tự tự nhiên ta được một lời giải của bài toán.
Nhậ xét: như ta thấy, bài toán tổng quát xếp N hậu được chia làm 4 trường hợp, và ta đã có cách xếp hậu cho mỗi trường hợp đó:
- 1 là với r(số dư) = 0,1,4,5,6,7,8,10,11
- 2 là với r = 3,9
- 3 là với r = 2
- 4 là với r = 8
Phần III: Áp dụng Hill-climbing để giải bài toán xếp n - queen:
a Cách lưu trữ dữ liệu (cấu trúc dữ liệu)
Thông thường chúng ta sẽ lưu bàn cờ dưới dạng mảng 2 chiều.Nhưng "trong bài toán xếp N hậu" thì cách lưu trữ này tỏ ra thiếu hiệu quả và tốn dữ liệu
Có một cách lưu trữ bàn cờ khác như sau:Tạo 1 mảng gồm N phần tử.Giá trị của mỗi phần tử phải đôi một khác nhau và phải < N Mỗi phần tử sẽ đại diện cho 1 quân hậu Trong đó chỉ số của phần tử trong mảng được coi là chỉ
số cột của con hậu tương ứng Giá trị của mỗi phần tử ta coi như là chỉ số hàng của con hậu tương ứng.Với cách lưu trữ này ta vừa có thể giảm số ô cần lưu trữ vừa loại đi rất nhiều trường hợp xếp hậu sai vị trí.Cụ thể như sau sau:
Giả sử ta có 1 cách xếp hậu chính xác (không con hậu nào ăn nhau) thì chúng (tức là tất cả N con hậu) mỗi con phải nằm trên 1 hàng và 1 cột Cách lưu bàn cờ như đã giới thiệu đảm bảo rằng tất cả N con hậu, mỗi con đứng
Trang 7trên 1 hàng và 1 cột riêng Cụ thể: vì chỉ số của mỗi phần tử trong mảng chạy từ 0->N-1 và luôn khác nhau.Mà như trên đã nói, mỗi phần tử đại diện cho 1 con hậu, chỉ số của mỗi phần tử là chỉ số cột của mỗi con hậu > N con hậu của chúng ta sẽ luôn có chỉ số cột chạy từ 0->N-1 và đôi 1 khác nhau Tương tự cho trường hợp chỉ số hàng tương ứng với giá trị của mỗi phần tử trong mảng.Nhưng vì giá trị của mỗi phần tử trong mảng có thể thay đổi và không chác đã khac nhau nên cần có 1 hàm kiểm tra để đảm bảo những giá trị của các phần tử luôn < N và đôi 1 khác nhau.Cách lưu trữ dữ liệu như đã nói còn tiện cho việc kiểm tra đường chéo.Vì như ta đã biết, N con hậu không chỉ đứng trên các hàng, cột khác nhau, mà chúng còn đứng trên các đường chéo khác nhau.Để biết được 2 con hậu có nằm trên 1 đường chéo không ta sử dụng định lý sau:
+ các phần tử nằm trên cùng một đường chéo song song với đường chéo chính có hiệu chỉ số hàng với chỉ số cột bằng nhau; (đường chéo chính là đường chéo từ phía trên bên trái xuống phía dưới tay phải)
+ các phần tử nằm trên cùng một đường chéo song song với đường chéo phụ
có tổng chỉ số hàng với chỉ số cột bằng nhau;(đường chéo phụ là đường chéo
từ phía trên bên phải xuống phía dưới bên trái)
thuật toán kiểm tra đường chéo như sau:
int arr[N] //mảng N con hậu
for(int i = 0;i < N; i++)
{
for(int j = i + 1;j < N; j++)
{
iF((arr + i == arr[j] + j) || (arr - i == arr[j] - j))
{
con hậu thứ i và con hậu thứ j bị cùng nằm trên 1
đường chéo
}
}
}
b Thuật toán hill-climbing khi áp dụng giải bài toán n – queen
Ý tưởng của thuật toán: Xếp tất cả N con Hậu lên bàn cờ (mỗi con hậu chếm
1 ô cờ) Mỗi một cách xếp như vậy được gọi là 1 State (trạng thái) Để lưu trữ các trạng thái ta dùng cấu trúc dữ liệu như đã trình bày trên phần 1.Tức
là mỗi trạng thái sẽ được lưu thành 1 mảng N phần tử, chỉ số của mỗi phần
tử là chỉ số cột của mỗi con hậu, giá trị của mỗi phần tử là chỉ số hàng của mỗi con hậu và giá trị của mỗi phần tử trong mảng phải khác nhau và phải <
Trang 8N Để sinh ra các trạng thái con của một trạng thái A nào đó, ta hoán đổi giá trị của phần tử thứ i và phần tử thứ j của mảng A.Để đánh giá 1 trạng thái của bàn cờ, ta đếm xem có bao nhiêu "cặp hậu" nằm thẳngtrên cùng một đường chéo.Bởi vì ta đã dùng cấu truc dữ liệu (như ở phần a đã nói), với cấu trúc này ta không cần thiết kiểm tra các con hậu nằm trên 1 hàng hoặc 1 cột Hàm đánh giá của chúng ta sẽ trả về số cặp quân hậu ăn nhau trên các đường chéo
Giả mã:
//hàm hillclimbing
Tạo 1 biến currentState //để lưu trạng thái hiện tại
Tạo mảng successor //để lưu trạng thái kế tiếp, ban đầu successor chứa trạng thái bắt đầu
do
{
if (successor.Count == 0)
{
//thông báo thất bại
break;
}
currState = successor[0];
//loại trạng thái ở đầu danh sách
successor.RemoveAt(0);
//tạo trạng thái gần kề với trạng
getSuccessor(currState, successor);
//thai currentState và thêm vào mảng successor theo thứ tự giảm dần của hàm đánh giá
} while (heuristics(currState)!= 0); //lặp cho đến khi gặp tráng thái kết thúc xuất hiện
Các hàm liên quan:
//Hàm đánh giá trạng thái: hàm đánh giá trạng thái trả về số lượng cặp quân hậu ăn lẫn nhau
private int heuristics(State aState) //truyền vào 1 trạng thái
{
int n = 0;
Trang 9for (i = 0; i < N - 1; i++)
{
for (j = i + 1; j < N; j++)
{
//Đếm số cặp quân hậu ăn nhau
if (aState.arrqueens[i] + i == aState.arrqueens[j] + j ||
aState.arrqueens[i] - i == aState.arrqueens[j] - j)
{
n++;
}
}
}
return n;
}
//Hàm tạo trạng thái getSuccessor, tạo ra trạng thái con của trạng thái aState và thêm nó vào successor theo thú tự giảm dần của hàm đánh giá
private void getSuccessor(State aState, State[] successor)
{
State childState; // trạng thái con
int p; // biến tạm
for (int i = 0; i < N; i++)
{
for (int j = i + 1; j < N; j++)
{
// tạo 1 trạng thái con mới
childState = new State(aState.arrqueens);
childState.h = heuristics(childState);
if (heuristics(childState) < heuristics(aState.h))
{
//chèn trạng thái con vào vị trí của nó
for (p = 0; p < successor.Count; p++)
{
if (heuristics(childState) < heuristics(successor[p]))
{
break;
}
}
successor.Insert(p, childState); //câu lệnh chèn
Trang 10}
}
}
}
HẾT
Bài viết là của sinh viên vũ minh đức; lớp tin hoc 5a trường HVKTQS Bài viết đã được tác giả đăng trên 1 số website
Tài liệu tham khảo: winkipedia và 1 số tài liệu khác