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

Báo cáo đồ án 1Phạm Duy Anh

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 (606.89 KB, 18 trang )

TRƯỜNG ĐẠI HỌC BÁCH KHOA HÀ NỘI
VIỆN CÔNG NGHỆ THÔNG TIN VÀ TRUYỀN THỒNG
----------o0o----------

Đồ án 1: Lập trình

Giảng viên hướng dẫn: TS.Ngô Lam Trung
Họ và tên: Phạm Duy Anh
MSSV: 20155076
Lớp: CN-CNTT2-K60

Hà Nội, tháng 6 năm 2017


MỤC LỤC
PHẦN MỞ ĐẦU ........................................................................................................3
CHUƠNG 1

GIỚI THIỆU ......................................................................................3

1.1 Các thuật toán sắp xếp .....................................................................................3
1.2 Nén và giải nén bằng giải thuật Huffman ........................................................3
1.3 Game Tic Tac Toe ...........................................................................................3
CHƯƠNG 2 PHÁT BIỂU VẤN ĐỀ ......................................................................4
2.1 Các thuật toán sắp xếp .....................................................................................4
2.1.1 Sắp xếp nổi bọt( Bubble sort) ...................................................................4
2.1.2 Sắp xếp chọn( select sort) .........................................................................5
2.1.3 Sắp xếp chèn ( Insert sort) ........................................................................6
2.1.5 Sắp xếp trộn( Merge sort) .........................................................................8
2.1.6 Sắp xếp vun đống( Heap sort).................................................................10
2.2 Nén và giải nén bằng giải thuật Huffman ......................................................13


2.2.1 Tổng quan ...............................................................................................13
2.2.2 Nén và giải nén .......................................................................................13
2.3 Game Tic Tac Toe .........................................................................................15
2.3.1 Giới thiệu ................................................................................................15
2.3.2 Phân tích và thiết kế ................................................................................16
CHƯƠNG 3 KẾT QUẢ VÀ ỨNG DỤNG ..........................................................18


PHẦN MỞ ĐẦU
Sau các đề tài của môn học Đồ án 1, em đã đạt được một số bài học và kĩ năng cho
bản thân. Trong quá trình làm đề tài này cùng với sự hướng dẫn tận tình của TS.
Ngô Lam Trung, em đã phát hiện và khắc phục được nhiều lỗi nhỏ. Các kĩ thuật xử
lý dữ liệu với file, pointer, …
Em xin gửi lời cảm ơn chân thành và sâu sắc tới TS. Ngô Lam Trung- giảng viên
hướng dẫn Đồ án 1: Lập trình đã giúp em rất nhiều trong quá trình thực hiện . Sự
hướng dẫn tận tình của thầy đã giúp em thu được nhiều kết quả tốt.

CHUƠNG 1 GIỚI THIỆU
Bài báo cáo về 3 vấn đề: Các thuật toán sắp xếp, Nén và giải nén bằng giải thuật
Huffman, và game Tic Tac Toe.

1.1 Các thuật toán sắp xếp
Các thuật toán sắp xếp cũng như các thuật toán tìm kiếm là một trong những
vấn đề được nhiều nhà khoa học quan tâm. Trong khoa học máy tính và trong
toán học, thuật toán sắp xếp là thuật toán sắp xếp các phần tử của một danh
sách hoặc mảng theo thứ tự tăng( hoặc giảm), và thường xét đến các trường
hợp là số.

1.2 Nén và giải nén bằng giải thuật Huffman
Trong khoa học máy tính và lý thuyết thông tin, mã hóa Huffman là một trong

những thuật toán mã hóa khởi đầu cho việc nén dữ liệu. nó dựa trên bảng tần
suất xuất hiện các kí tự cần mã hóa để xây dựng một bộ mã nhị phân cho các kí
tự trên sao cho dung lượng( tổng số bit) là nhỏ nhất.

1.3 Game Tic Tac Toe


Tic Tac Toe là một trong những trò chơi khá phổ biến hiện nay, được chơi trên
bàn cờ có kích thước 3x3. Hai người chơi, một người dùng kí hiệu O, một
người dùng kí hiệu X, lần lượt điền kí hiệu của mình vào các ô còn trống.
Người thắng cuộc là người đầu tiên tạo ra được 3 nước cờ ngang, dọc hoặc
chéo.

CHƯƠNG 2

PHÁT BIỂU VẤN ĐỀ

2.1 Các thuật toán sắp xếp
2.1.1 Sắp xếp nổi bọt( Bubble sort)
Sắp xếp nổi bọt là một thuật toán sắp xếp đơn giản, với thao tác cơ bản là so
sánh các cặp phần tử liền nhau, nếu chúng chưa đứng đúng vị trí thì đổi vị trí (
swap) của chúng cho nhau. Có thể tiến hành từ trên xuống( bên trái sang) hoặc
từ dưới lên( bên phải sang). Sắp xếp nổi bọt sử dụng phép so sánh các phần tử
nên là một giải thuật so sánh.
a) Giải thuật
Giả sử dãy cần xếp có n phần tử. Ta bắt đầu so sánh từ cặp phần tử 1 và 2,
cho đến cặp phần tử n-1 và n, nếu phần tử đứng trước lớn hơn phần tử đứng sau
thì ta swap. Sau n-1 bước so sánh này thì phần tử cuối cùng chính là phần tử
lớn nhất.
Ta tiếp tục so sánh như vậy với dãy n-1 số đầu của dãy vừa được sắp xếp ở

trên.

( Trong một lần duyệt mà không cần swap bất cứ lần nào có nghĩa là dãy đã
được sắp xếp)
b) Mã giả
procedure swap(var a, b:integer)
var temp:integer;
temp=a;a=b;b=a;
endprocedure

procedure bubbleSort(list L, number n)
for number i from n downto 2

//n là số phần tử của dãy


for number j from 1 to (i - 1)
if L[j] > L[j + 1] //nếu chúng không đúng thứ tự
swap(L[j], L[j + 1]) //đổi chỗ chúng cho nhau
endif
endfor
endfor
endprocedure

c) Độ phức tạp
Với mỗi i=n, n-1, …, 2 ta cần i-1 phép so sánh. Vì vậy số các phép so sánh cần
(𝑛−1).𝑛

thực hiện là: 1+2+3+…+n-1=


2

.

Vậy độ phức tạp của thuật toán bằng O(n2).
2.1.2 Sắp xếp chọn( select sort)
Sắp xếp chọn là một thuật toán sắp xếp đơn giản dựa trên việc so sánh tại
chỗ( là sắp xếp không cần dùng thêm mảng phụ).
Chọn phần tử nhỏ nhất trong n phần tử ban đầu đưa về vị trí thứ nhất của dãy
số. Tiếp tục chọn phần tử nhỏ nhất trong n-1 phần tử còn lại xếp vào vị trí thứ
hai của dãy,… tiếp tục chọn như vậy cho đến khi n-1 phần tử đứng đúng vị trí.
a) Giải thuật
Bước 1: i←1
Bước 2: tìm phần tử amin trong dãy số từ i→n
Bước 3: hoán đổi vị trí a[i] với amin
Bước 4: nếu i<=n-1, i←i+1 quay lại bước 2, ngược lại dừng.

procedure selectSort(list L, number n)

//n=listsize


For number i from 1 to n-1
for number j from i+1 to n
if L[i] > L[j] //nếu chúng không đúng thứ tự
swap(L[i], L[j]) //đổi chỗ chúng cho nhau
endif
endfor
endfor
endprocedure


c) Độ phức tạp
Với mỗi i=1,2,…,n-1 cần thực hiện n-1, n-2,…,1 phép so sánh. Suy ra cần
(𝑛−1).𝑛

1+2+3+…+n-1=

2

phép so sánh.

Vậy độ phức tạp là O(n2).
2.1.3 Sắp xếp chèn ( Insert sort)
Sắp xếp chèn là thuật toán sắp xếp có nét tương đồng với việc sắp xếp các
quân bài của người chơi bài. Muốn sắp xếp các quân bài theo một thứ tự nhất
định thì từ quân bài thứ 2 người chơi sẽ so sánh với các quân bài trước đó để
chèn nó vào vị trí đúng.
a) Giải thuật
Giả sử danh sách gồm n phần tử. Danh sách có 1 phần tử là danh sách đã
được sắp xếp. Giả sử danh sách a1,…,an-1 đã được sắp xếp. bây giờ ta cần
chèn phần tử an vào vị trí thích hợp để được danh sách sắp xếp đúng. Vị trí
chèn đúng là vị trí mà phần tử an lớn hơn hoặc bằng các phần tử đứng trước
nó, nhỏ hơn hoặc bằng các phần tử đứng sau nó( nếu có).
b) Mã giả
Procedure insert(array a, int k, value) {
int i:= k-1;
while (i > 0 and a[i] > value) {
a[i+1]:= a[i];
i:= i - 1;
}

a[i+1]:= value;
}

Procedure insertSort(array a, int length) {
int k:= 2;


while (k < length) {
insert(a, k, a[k]);
k:= k + 1;
}
}

c) Độ phức tạp
Độ phức tạp của thuật toán Sắp xếp chèn là O(n2).
2.1.4 Sắp xếp nhanh( Quick sort)
Sắp xếp nhanh, còn được gọi là sắp xếp kiểu phân chia( part sort) là một
thuật toán được phát triển bởi C.A.R Hoare. Danh sách được chia thành hai danh
sách con. Chọn một phần tử của danh sách làm chốt, những phần tử nhỏ hơn hoặc
bằng chốt được đưa về bên trái chốt thuộc dãy con thứ nhất, những phần tử lớn hơn
chốt đưa về bên phải chốt thuộc dãy con thứ hai. Tiếp tục như vậy cho đến khi độ
dài các danh sách con đều bằng 1.
a) Giải thuật
• Phần tử chốt: cách tìm phần tử chốt ảnh hưởng khá lớn đến hiệu quả của
chương trình. Vậy cần phải chọn phần tử chốt như thế nào để đạt được
hiệu quả tốt nhất( sau log2(length) lần chia để được các danh sách con có
độ dài bằng 1). Ta có một vài cách chọn phần tử chốt như sau:
- Chọn phần tử đứng đầu hoặc cuối làm phần tử chốt.
- Chọn phần tử đứng giữa danh sách làm phần tử chốt
- Chọn phần tử trung vị trong 3 phần tử đầu, giữa và cuối làm phần tử

chốt.
- Chọn phần tử ngẫu nhiên trong danh sách làm phần tử chốt.
• Thuật phân chia: Chọn phần tử chốt xong rồi ta sẽ chia như thế nào?
1. Duyệt từ đầu đến cuối và so sánh từng phần tử của danh sách
với phần tử chốt, như vậy ta cần thực hiện n phép so sánh và
mất n không gian bộ nhớ cho các giá trị trung gian.
2. Duyệt theo hai đường, một đường từ đầu, một đường từ cuối
danh sách. Đổi chỗ phần tử lớn hơn chốt đầu tiên từ bên trái với
phần tử nhỏ hơn hoặc bằng chốt đầu tiên từ bên phải, lặp đến
khi hai đường giao nhau.


b) Mã giả
Thuat phan chia:
//left la dau danh sach, right la cuoi danh sach
Function partition (array, left, right, pivotIndex){
pivotValue:=array[pivotIndex];
swap(array[pivotindex, array[right]);//chuyen chot ve cuoi
storeIndex:=left;
for i:=left to right-1
if(array[i]swap(array[i], array[storeIndex]);
storeIndex+=1;
}
Swap( array[pivotIndex], array[storeIndex]);
Return storeIndex;

Quick sort:
function quickSort(array, left, right)
if (left< right)

choose any pivotIndex such that left ≤ pivotIndex ≤ right;
pivotNewIndex:= partition(array, left, right, pivotIndex);
quicksort(array, left, pivotNewIndex-1);
quicksort(array, pivotNewIndex, right);

c) Độ phức tạp
Độ phức tạp của thuật toán thường là O(nlog(n)).
2.1.5 Sắp xếp trộn( Merge sort)
Sắp xếp trộn là thuật toán sắp xếp nâng cao, sử dụng để sắp xếp các danh sách,
luồng tập tin… theo một trật tự nào đó. Sắp xếp trộn được xếp vào loại sắp xếp so
sánh. Thuật toán này là một ví dụ tương đối điển hình cho lối thuật toán chia để trị
mà John Von Neumann đưa ra vao những năm 1945.
a) Giải thuật
Các bước thực hiện thuật toán trộn tự nhiên như sau:
Bước 1: // Chuẩn bị


r = 0; // r dùng để đếm số đường chạy
Bước 2:
Tách dãy a1, a2,..., an thành 2 dãy b, c theo nguyên tắc luân phiên từng
đường chạy:
Bước 2.1:
Phân phối cho b một đường chạy; r = r+1;
Nếu a còn phần tử chưa phân phối
Phân phối cho c một đường chạy; r = r+1;
Bước 2.2:
Nếu a còn phần tử: quay lại bước 2.1;
Bước 3:
Trộn từng cặp đường chạy của 2 dãy b, c vào a.
Bước 4:

Nếu r <= 2 thì trở lại bước 2;
Ngược lại: Dừng;
b) Mã giả
Procedure Merge(k1,k2,k3);
Var i,j,k:integer;
T: array[k1..k3] of integer;
Begin
i:=k1;
j:=k2;
k:=k3;
while iBegin
if a[i]<=a[j] then

begin

T[k]=a[i];
i=i+1;
End
else

begin

T[k]=a[j];
j=j+1;
End;
k=k+1;
End;
if i>=k2 then
while k<=k3 do begin



T[k]=a[j];
j=j+1;
k=k+1;
End
if j>k3 then
while kT[k]=a[i];
i=i+1;
k=k+1;
End
For k=k1 to k3
a[k]=T[k];
End

Procedure MergeSort (k1,k2)
Var k3:integer;
Begin
if k1k3:=(k1+k2)/2;
MergeSort(k1,k3);
MergeSort(k3+1,k2);
Merge(k1,k3,k2);
End;
End;

c) Độ phức tạp
Độ phức tạp của thuật toán thường là O(nlog(n)).
2.1.6 Sắp xếp vun đống( Heap sort)

Sắp xếp vun đống (Heapsort) dựa trên một cấu trúc dữ liệu được gọi là đống nhị
phân (binary heap), gọi đơn giản là đống. Trong mục này chỉ nói về đống trong bài
toán sắp xếp.
a) Giải thuật
Đống:
• Mỗi mảng a[1..n] có thể xem như một cây nhị phân gần đầy (có trọng số là
các giá trị của mảng), với gốc ở phần tử thứ nhất, con bên trái của đỉnh a[i]


là a[2*i] con bên phải là a[2*i+1] (nếu mảng bắt đầu từ 1 còn nếu mảng bắt
đầu từ 0 thì 2 con là a[2*i+1] và a[2*i+2]) (nếu 2*i<=n hoặc 2*i+1<=n, khi
𝑛
đó các phần tử có chỉ số lớn hơn int( ) không có con do đó là lá).
2
• Một cây nhị phân, được gọi là đống cực đại nếu khóa của mọi nút không nhỏ
hơn khóa các con của nó. Khi biểu diễn một mảng a[] bởi một cây nhi phân
theo thứ tự tự nhiên điều đó nghĩa là a[i]>=a[2*i] và a[i]>=a[2*i+1] với mọi
𝑛
i =1.. int( ) Ta cũng sẽ gọi mảng như vậy là đống. Như vậy trong đống a[1]
2
(ứng với gốc của cây) là phần tử lớn nhất. Mảng bất kỳ chỉ có một phần tử
luôn luôn là một đống.
• Một đống cực tiểu được định nghĩa theo các bất đẳng thức ngược lại:
a[i]<=a[2*i] và a[i]<=a[2*i+1]. Phần tử đứng ở gốc cây cực tiểu là phần tử
nhỏ nhất.
Vun đống:
• Việc sắp xếp lại các phần tử của một mảng ban đầu sao cho nó trở thành
đống được gọi là vun đống.
• Vun đống tại đỉnh thứ i: Nếu hai cây con gốc 2*i và 2*i+1 đã là đống thì
để cây con gốc i trở thành đống chỉ việc so sánh giá trị a[i] với giá trị lớn

hơn trong hai giá trị a[2*i] và a[2*i+1], nếu a[i] nhỏ hơn thì đổi chỗ
chúng cho nhau. Nếu đổi chỗ cho a[2*i], tiếp tục so sánh với con lớn hơn
trong hai con của nó cho đên khi hoặc gặp đỉnh lá.
• Vun một mảng thành đống: Để vun mảng a[1..n] thành đống ta vun từ
𝑛
dưới lên, bắt đầu từ phần tử a[j]với j = int( ) ngược lên tới a[1].
2
• Sắp xếp bằng vun đống:
- Đổi chỗ (Swap): sau khi mảng a[1..n] đã là đống, lấy phần tử a[1]
trên đỉnh của đống ra khỏi đống đặt vào vị trí cuối cùng n, và
chuyển phần tử thứ cuối cùng a[n] lên đỉnh đống thì phần tử a[n]
đã được đứng đúng vị trí.
- Vun lại: phần còn lại của mảng a[1..n-1] chỉ khác cấu trúc đống ở
phần tử a[1]. Vun lại mảng này thành đống với n-1 phần tử.


- Lặp: tiếp tục với mảng a[1..n-1]. Quá trình dừng lại khi đống chỉ
còn lại một phần tử.
b) Mã giả
#include <stdio.h>
#include <conio.h>
const int n = 10;
typedef int keytype; typedef float othertype;
typedef struct recordtype{
keytype key;
othertype otherfields;
};
recordtype a[n]; // khai bao mang a co n phan tu
void Swap(recordtype &x, recordtype &y){
recordtype temp;

temp = x;
x = y;
y = temp;
}
void PushDown(int first, int last){ //while (r <= (last - 1) / 2)
if (first == last || first > (last - 1) / 2) return;
if (last == 2 * first + 1) {
if (a[first].key > a[last].key)
Swap(a[first], a[last]);
return; //first = last;
}else
if ((a[first].key > a[2*first+1].key) && a[2*first+1].key
<= a[2*first+2].key)){
Swap(a[first], a[2*first+1]);
PushDown(2 * first + 1, last);
}else
if ((a[first].key > a[2*first+2].key) && a[2*first+2].key <
a[2*first+1].key)){
Swap(a[first], a[2*first+2]);
PushDown(2 * first + 2, last);
}
else return; //first = last;
}
void HeapSort(){
int i;


for(i = (n-2) / 2; i >= 0; i--) PushDown(i, n-1);
for(i = n-1; i>=2; i--) {
Swap(a[0], a[i]);

PushDown(0, i-1);
}
Swap(a[0], a[1]);
}

2.2 Nén và giải nén bằng giải thuật Huffman
2.2.1 Tổng quan
Để mã hóa dữ liệu( các kí tự, chữ số…) ta thay chúng bằng các xâu nhị phân,
được gọi là từ mã của các dữ liệu trên. Theo ASCII, mỗi kí tự hay chữ số đều được
mã hóa bằng xâu nhị phân dài 8 bit, vd: ‘a’= ‘01100001’, ‘b’= ‘01100010’. Trong
một văn bản, tần suất của các kí tự và chữ số là rất khác nhau, ví dụ như một văn
bản tần suất của ‘a’ là 100000, của ‘B’ là 1, ‘c’ là 100, nhưng số bit danh cho mỗi
kí tự đều bằng 8, suy ra tổng số bit dùng cho ba kí tự trên là 800808 bit. Với tần
suất không đều như trên, ta có thể tiết kiệm dung lượng bằng cách những kí tự có
tần suất lớn thì mã hóa với độ dài nhỏ, ngược lại mã hóa với độ dài lớn. Giả sử với
ví dụ trên, mã hóa ‘a’ dài 4 bit, ‘c’ 6 bit và ‘B’ 8 bit thì tổng số bit sử dụng là:
400608<< 800808. Để tiết kiệm bộ nhớ ta sẽ mã hóa các kí tự và chữ số với độ dài
bit khác nhau( không giống với mã ASCII nữa), nhưng khi giải mã ta phải làm thế
nào. Nếu ta chèn thêm dấu ‘,’ để phân biệt các kí tự với nhau thì dung lượng khi ấy
còn lớn hơn cả dung lượng văn bản gốc. Từ đó dẫn ta đến khái niệm ‘Mã tiền tố’.
Mã tiền tố là bộ từ mã của một tập hợp kí hiệu sao cho từ mã của kí hiệu này
không là tiền tố( phần đầu) từ mã của một kí hiệu khác.
• Biểu diễn mã tiền tố trên cây nhị phân
Một cây nhị phân gồm n lá ta có thể tạo bộ mã tiền tố cho n kí hiệu sao
cho tại các node lá là các kí hiệu. Từ mã của kí hiệu được tạo ra bằng việc
đi từ gốc tới lá, duyệt sang con trái thêm 0 , duyệt sang con phải thêm 1.
2.2.2 Nén và giải nén
a) Giải thuật



Ta sử dụng giải thuật tham lam để xây dựng cây mã Huffman. Ở mỗi bước
ta chọn hai kí hiệu có tần suất thấp nhất để mã hóa cho từ mã dài nhất. Giả
sử tập A gồm n kí hiệu và có trọng số tương ứng Wi, i=1..n.
• Khởi tạo rừng n cây với mỗi cây chỉ gồm mỗi node gốc chứa kí hiệu đó và
tần suất của kí hiệu đó Wi.
• Chọn hai cây mà trọng số( tần suất) nhỏ nhất rồi hợp thành một cây mới
bằng cách thêm một node gốc mới nối với hai node con là hai node vừa
chọn. Trọng số của gốc mới bằng tổng trọng số của hai cây vừa chọn( gốc
mới này không chứa kí hiệu).
Sau mỗi bước số cây trong rừng giảm đi một. Khi chỉ còn một cây thì cây đó
chính là cây mã tiền tố tối ưu cần xây dựng.
d) Mã giả
typedef struct node{
char _bit;
long freq;
char CH;
struct node *pLeft;
struct node *pRight;
}NODE;
//------------------void makeTreeHuff(NODE *huff[]){
int length;//so luong ki tu co trong van ban
while(length >1) {
find NODE min1, min2;//tim hai cay co trong so nho nhat
NODE *newNode=min1;
min1->pRight=newNode;
min1->pLeft=min2;
min1->freq=min2->freq+newNode->freq;
length=length-1;//rung giam di mot cay
}
//-----------------------


void setPrefixCode(NODE *treeHuff, char *strCode){
if(treeHuff->pLeft==NULL && treeHuff->pright==NULL)
prefixCodeOfChar =strCode;


else setPrefixCode(treeHuff->pLeft,strCode1);
//strCode1=strCode them 0
setPrefixCode(treeHuff->pRight, strCode2);
//strCode2=strCode them 1
}

e) Nén file bằng mã Huffman
Sau khi đã xây dựng được bộ mã Huffman của tập hợp các kí hiệu có trong
file, giả sử ta lưu bộ mã đó trong mảng prefixCode[i], i=1..n. Ta sẽ nén file
theo các bước như sau:
1. Đọc từng byte của file cần nén cho đến khi hết tệp
2. Chuyển theo bộ mã Huffman thành xâu nhị phân
3. Ghép với xâu nhị phân còn dư từ bước trước
4. Nếu có đủ 8 bit trong xâu thì cắt ra 8 bit đầu tiên ghi vào tệp nén
5. Nếu đã hết tệp cần nén thì dừng.
Trong khi nén, nếu đến khi đọc đến cuối tẹp cần nén, số bit trong xâu nhị
phân lại không chia hết cho 8, mà đủ 8 bit trong xâu thì ta mới cắt ra 8 bit để ghi
vào tệp nén, vậy số bit thừa này ta sẽ xử lí ra sao? Em sẽ chèn thêm một số bit 0
vào cuối các bit trên trong xâu sao cho độ dài bằng 8. Chuyển nó sang mã ASCII
rồi ghi ra file nén, ghi thêm dộ dài số bit 0 ta thêm vào. Như vậy khi giải nén ta lấy
hai giá trị cuối trong tệp nén để giải quyết các bit thừa nói ở trên.
• Giải thuật để dịch ngược chuỗi bit dựa vào cây Huffman:
Đi từ gốc cây Huffman, đọc từng bit của tệp nén:
- Nếu là bit 0, rẽ sang nhánh trái

- Nếu là bit 1, rẽ sang nhánh phải
- Nếu là node lá, in ra tệp giải nén kí hiệu đó.

2.3 Game Tic Tac Toe
2.3.1 Giới thiệu
Tic tac toe là một trò chơi khá phổ biến viết trên bàn cờ có 9 ô (3x3). Hai người
cùng chơi, một người dùng kí hiệu O, người kia dùng kí hiệu X, lần lượt điền ký


hiệu của mình vào các ô. Người thắng cuộc là người đầu tiên tạo được một dãy 3
ký hiệu của mình theo các chiều ngang, dọc hay chéo đều được. Nếu sau khi đã lấp
đầy các ô trống mà vẫn không có ai đạt được một dãy 3 ô thẳng hàng thì sẽ là hòa.
Hình sau mô tả 3 trường hợp ví dụ:

2.3.2 Phân tích và thiết kế
Giả sử rằng ban đầu tất cả các ô đều trống trên bàn cờ( board), người chơi dùng
kí hiệu X, còn máy dùng kí hiệu O. Để thực hiện mỗi bước đi, người chơi sẽ sử
dụng các phím điều khiển ←↕→ trên bàn phím, để đánh nước đi đó người chơi
nhấn phím Enter, nếu ô đó còn trống thì nước đi X được ghi nhận, ngược lại không
ghi nhận và người chơi phải đánh vào vị trí khác.
Đó là phần điều khiển và chơi dành cho người chơi, vậy đối với máy thì sao? Ta
có thể làm tương tự được không? Nếu tương tự như vậy thì sẽ chẳng khác nào hai
người chơi chơi với nhau. Vì vậy cần phải có chút thay đổi khác của phần máy so
với phần người chơi. Ta sử dụng thuật toán Min-Max để giúp máy có trí tuệ nhân
tạo và chơi như con người. Thuật toán Min-Max là gì?
Trong 2 người chơi thì một người gọi là người chơi cực đại( MAX) và đối thủ
của họ là người chơi cực tiểu( MIN). Cả hai đấu thủ đều cố gắng đi những nước
thế nào sao cho điểm tuyệt đối của mình lớn hơn hay cao nhất có thể. Tức là người
chơi MAX cố gắng làm cho điểm của mình cao hơn và làm cho điểm của đối thủ
bớt âm hơn ( giảm về trị số), ngược lại, người chơi MIN cố gắng làm cho điểm của

mình âm hơn và làm giảm điểm của đối thủ.


Giải thuật tìm kiếm Min-Max được sử dụng để tìm kiếm tất cả các “diễn biến”
tiếp theo của trò chơi cho đến tầng được yêu cầu. Điểm số ban đầu được gán cho
lá, sau đó bằng cách lượng giá các nước đi, điểm số được gán cho các tầng ở trên
qua giải thuật Min-Max, thuật giải thực hiện một lát cắt cho trước và tính điểm trên
đó.
Ý tưởng cơ bản của thuật giải Min-Max theo đệ quy:
- Nếu mức đang xét là người chơi cực tiểu thì áp dụng thuật toán MinMax cho các con của nó. Lưu kết quả là giá trị nhỏ nhất.
- Nếu mức đang xét là người chơi cực đại thì ấp áp dụng thuật toán
Min-Max cho các con của nó. Lưu kết quả là giá trị lớn nhất.
- Nếu mức đang xét là lá thì tính giá trị tĩnh của thế cờ hiện tại ứng
với người chơi lúc đó. Lưu giữ kết quả.
Mã 1:
MinMax (int x){
//x la node muon tinh diem
if x is left
return score of x;
else
if x in a MinNode
for all children of x:v1,v2, …,vn
return min(MinMax(v1), MinMax(v2), …, Minmax(vn))
else
for all children of x:v1,v2, …,vn
return max(MinMax(v1), MinMax(v2), …, Minmax(vn))
}

Do số lượng nước đi tối đa của trò chơi Tic tac toe là 9, là số lượng rất
nhỏ nên ta có thể tìm kiếm tất cả các node, mà không cần thêm tham số độ

sâu( depth) để giới hạn số lượng node.


CHƯƠNG 3

KẾT QUẢ VÀ ỨNG DỤNG

Như vậy qua bài báo cáo này, em đã đạt được một số kết quả như sau:
1. Về các thuật toán sắp xếp, trước hết, với một dãy số được sinh ra một
cách ngẫu nhiên, một số lượng lớn, cùng với việc đánh giá độ phức
tạp của các thuật toán trên lý thuyết, nhìn vào kết quả khi chạy
chương trình, ta so sánh được thời gian chạy của từng thuật toán với
cùng dãy số đó. Từ đó ta biết được thuật toán nào chạy nhanh hơn,
thuật toán nào chạy chậm hơn, và trong trường hợp nào thì thuật toán
nào là tối ưu nhất.
2. Về phần nén và giải nén bằng giải thuật Huffman, sau khi nén tệp cần
nén em nhận thấy tổng dung lượng tiết kiệm được thường lớn hơn
40% tổng dung lượng ban đầu của tệp cần nén. Không những vậy, em
còn học hỏi được nhiều kiến thức hơn về các cách xử lý trên file.
3. Về game Tic Tac Toe, em đã biết ứng dụng thuật toán Min-Max để
xây dựng trò chơi này cùng với một số kĩ năng khác để tạo ra một trò
chơi đơn giản giữa người với máy có trí tuệ như con người.

TÀI LIỆU THAM KHẢO
/> /> />


×