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

Đồ án Giải thuật lập trình Sudoku 9x9 (Có code)

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

TRƯỜNG ĐẠI HỌC BÁCH KHOA
KHOA CÔNG NGHỆ THÔNG TIN

ĐỒ ÁN
GIẢI THUẬT VÀ LẬP TRÌNH
Đề tài: GIẢI SUDOKU 9x9

GIẢNG VIÊN HƯỚNG DẪN:

VÕ ĐỨC HOÀNG

SINH VIÊN THỰC HIỆN:
TRẦN VĂN HỒNG QUÂN

LỚP: 16T3 NHÓM: 11C

ĐOÀN VĂN SINH

LỚP: 16T3 NHÓM: 11C

Đà Nẵng, 11/2018


Đề tài: Giải Sudoku 9x9

Đồ án Giải thuật và Lập trình

MỤC LỤC
MỤC LỤC..............................................................................................................1
LỜI MỞ ĐẦU.........................................................................................................2
DANH MỤC HÌNH VẼ..........................................................................................3


1.

GIỚI THIỆU ĐỀ TÀI......................................................................................4

2.

CƠ SỞ LÝ THUYẾT......................................................................................5

3.

4.

2.1.

Ý tưởng....................................................................................................5

2.2.

Cơ sở lý thuyết.........................................................................................6

TỔ CHỨC CẤU TRÚC DỮ LIỆU VÀ THUẬT TOÁN.................................6
3.1.

Phát biểu bài toán.....................................................................................6

3.2.

Cấu trúc dữ liệu........................................................................................6

3.3.


Thuật toán Quay lui (Backtracking).........................................................9

CHƯƠNG TRÌNH VÀ KẾT QUẢ..................................................................9
4.1.

Tổ chức chương trình.............................................................................10

4.2.

Ngôn ngữ cài đặt....................................................................................17

4.3.

Kết quả...................................................................................................17

4.3.1. Giao diện chính của chương trình.....................................................17
4.3.2. Kết quả thực thi của chương trình.....................................................18
4.3.3. Nhận xét............................................................................................20
5.

KẾT LUẬN VÀ HƯỚNG PHÁT TRIỂN......................................................21
5.1.

Kết luận..................................................................................................21

5.2.

Hướng phát triển....................................................................................21


TÀI LIỆU THAM KHẢO.....................................................................................22

Trang 1


Đề tài: Giải Sudoku 9x9

Đồ án Giải thuật và Lập trình

LỜI MỞ ĐẦU
Đề tài Sudoku 9x9 sử dụng ngôn ngữ lập trình C thể hiện các thuật toán đệ quy và quay
lui bằng công cụ lập trình DEV C++ để giải một bài toán Sudoku 9x9 cho trước.
Thông qua báo cáo này, chúng em xin gửi lời cảm ơn đến thầy Võ Đức Hoàng – giảng
viên hướng dẫn đã hỗ trợ tận tình và giải đáp các thắc mắc trong suốt quá trình làm đồ án Giải
thuật và Lập trình.
Tuy nhiên, do kiến thức còn hạn hẹp, mặc dù đã nỗ lực hết sức mình, nhưng chắc rằng đồ
án khó tránh khỏi thiếu sót. Chúng em rất mong nhận được sự thông cảmvà chỉ bảo tận tình
của quý Thầy cô và các bạn.
Xin chân thành cảm ơn.

Trang 2


Đề tài: Giải Sudoku 9x9

Đồ án Giải thuật và Lập trình

DANH MỤC HÌNH VẼ
Hình 1. Ví dụ một đề bài Sudoku 9x9.
Hình 2. Đáp án câu đố.

Hình 3. Cây tìm kiếm quay lui.
Hình 4. Mảng S.
Hình 5. Giao diện chương trình chính.
Hình 6. Đầu vào input.txt
Hình 7. Lựa chọn 1: Giải từng bước – Xem tiến trình bước 1 – Đáp án 1
Hình 8. Lựa chọn 1: Giải từng bước - Xem tiến trình bước 2 – Đáp án 2
Hình 9. Lựa chọn 2: Xem đáp án 1
Hình 10. Lựa chọn 2: Xem đáp án 2
Hình 11. So sánh kết quả và đầu ra output.

Trang 3


Đề tài: Giải Sudoku 9x9

Đồ án Giải thuật và Lập trình

1. GIỚI THIỆU ĐỀ TÀI
Sudoku là một từ Nhật, có thể dịch tạm là con số độc nhất, (Thật ra nó có nguồn gốc
từ Mỹ với tên gọi là "đặt con số vào vị trí đúng"). Đây là một trò chơi trí tuệ nổi tiếng,
thu hút nhiều người tham gia thuộc nhiều tầng lớp, độ tuổi khác nhau.
Sudoku có nhiều biến thể khác nhau: 3x3, 4x4, 6x6, 8x8, 9x9, 12x12, 16x16,… Đối
với đề tài này, chúng em áp dụng cho sudoku dạng chuẩn 9x9.
Bảng câu đố hình vuông, mỗi chiều có 9 ô nhỏ, hợp thành 9 cột, 9 hàng và được
chia thành 9 ô lớn 3x3. Một vài ô nhỏ được đánh số, đó là những manh mối duy nhất để
bạn tìm lời giải. Tuỳ theo mức độ nhiều hay ít của các manh mối, các câu đố được xếp
loại dễ, trung bình, khó hay cực khó.

Hình 1. Ví dụ một đề bài Sudoku 9x9
Cách chơi Sudoku là điền các số từ 1 đến 9 vào các ô trống theo quy luật đơn giản:

-

Các ô ở mỗi hàng (ngang) phải có đủ các con số từ 1 đến 9 không cần theo thứ

-

tự.
Các ô ở mỗi hàng (dọc) phải có đủ các con số từ 1 đến 9 không cần theo thứ tự.
Mỗi miền 3x3 được viền đậm phải có đủ các số từ 1 đến 9.

Trang 4


Đề tài: Giải Sudoku 9x9

Đồ án Giải thuật và Lập trình

Hình 2. Đáp án câu đố

2. CƠ SỞ LÝ THUYẾT
2.1. Ý tưởng
Chương trình giải dựa trên thuật toán quay lui. Bằng việc liệt kê các tình huống,
thử các khả năng có thể cho đến khi tìm thấy một lời giải đúng, thuật toán quay lui chia
nhỏ bài toán, lời giải của bài toán lớn sẽ là kết quả của việc tìm kiếm theo chiều sâu của
tập hợp các bài toán phần tử. Trong suốt quá trình tìm kiếm nếu gặp phải một hướng nào
đó mà biết chắc không thể tìm thấy đáp án thì quay lại bước trước đó và tìm hướng khác
kế tiếp hướng vừa tìm kiếm đó. Trong trường hợp không còn một hướng nào khác nữa thì
thuật toán kết thúc.
2.2. Cơ sở lý thuyết
2.2.1. Thuật toán Quay lui (Backtracking)

Quay lui là một chiến lược tìm kiếm lời giải cho các bài toán thỏa mãn ràng buộc.
Các bài toán thỏa mãn ràng buộc là các bài toán có một lời giải đầy đủ, trong đó thứ
tự các phần tử không quan trọng. Các bài toán này bao gồm một tập các biến mà mỗi
biến cần được gán một giá trị tùy theo các ràng buộc cụ thể của bài toán. Việc quay lui là
thử tất cả các tổ hợp để tìm một lời giải. Thế mạnh của phương pháp này là nhiều cài đặt
tránh được việc phải thử nhiều tổ hợp chưa hoàn chỉnh, và nhờ đó giảm thời gian chạy,
tìm được nhiều đáp án cho những bài toán có nhiều cách giải.
Trang 5


Đề tài: Giải Sudoku 9x9

Đồ án Giải thuật và Lập trình

Đó là một quá trình tìm kiếm độ sâu trong một tập hợp các lời giải. Trong quá trình
tìm kiếm, nếu ta gặp một hướng lựa chọn không thỏa mãn, ta quay lui về điểm lựa chọn
nơi có các hướng khác và thử hướng lựa chọ tiếp theo. Khi đã thử hết các lựa chọn xuất
phát từ điểm lựa chọn đó, ta quay lại điểm lựa chọn trước đó và thử hướng lựa chọn tiếp
theo tại đó. Quá trình tìm kiếm thất bại khi không còn điểm lựa chọn nào nữa
Quy trình đó thường được cài đặt bằng một hàm đệ quy mà trong đó mỗi thể hiện
của hàm lấy thêm một biến và lần lượt gán tất cả các giá trị có thể cho biến đó, với mỗi
lần gán giá trị lại gọi chuỗi đệ quy tiếp theo để thử các biến tiếp theo. Chiến lược quay lui
tương tự với tìm kiếm theo độ sâu nhưng sử dụng ít không gian bộ nhớ hơn, nó chỉ lưu
trữ trạng thái của một lười giải hiện tại và cập nhật nó.

Hình 3. Cây tìm kiếm quay lui
- Ở một bài toán hiện tại (mỗi nốt), ta đi tìm lời giả cho bài toán đó. Ứng với lời
giải, ta đi giải bài toán kế tiếp cho đến khi bài toán gốc trở nên đầy đủ.
- Lời giải của bài toán gốc thường là một lối đi từ gốc đến nốt cuối cùng.


3. TỔ CHỨC CẤU TRÚC DỮ LIỆU VÀ THUẬT TOÁN
3.1. Phát biểu bài toán
-

Đầu vào : Đề Sudoku đọc bảng từ file , hoặc do người dùng nhập.

-

Đầu ra (Kết quả): Đáp án có thể là các bước giải Sudoku của chương trình hoặc đáp
án toàn bộ chương trình (có thể ra nhiều đáp án nếu đầu vào có nhiều đáp án)

Trang 6


Đề tài: Giải Sudoku 9x9

Đồ án Giải thuật và Lập trình

3.2. Cấu trúc dữ liệu
1. Biến
Sử dụng biến kiểu mảng hai chiều để thể hiện vị trí của các phần tử đang xét.
Mảng hai chiều là một mảng mà các phần tử của nó là mảng một chiều, giống như
một bảng gồm có dòng và cột, được đánh dấu vị trí là các chỉ số bao gồm chỉ số dòng và
chỉ số cột trong đó.
Trong ngôn ngữ C, mảng hai chiều được khai báo theo cú pháp:
data_type

array_name[num1][num2];

- với data_type là kiểu dữ liệu của các phần tử trong mảng.

- num1 số mảng một chiều có trong mảng.
- num2 số phần tử có trong mỗi mảng một chiều .
Ví dụ mảng hai chiều S trong bài được khai báo như sau:
int S[9][9];
Khi đó mảng S giống như một bảng có 9 hàng và 9 cột (mỗi hàng có 9 phần tử), và mỗi
phần tử trong mảng là một số kiểu int

0

1

2

3

4

5

6

7

8

0
1
2
3
4

5
6
7
8
Hình 4. Mảng S
Muốn truy cập đến một phần tử trong mảng ta dùng cú pháp S[i][j]. Khi đó S[i][j]
chứa giá trị của ô ở vị trí hàng i cột j
2. Hàm

Trang 7


Đề tài: Giải Sudoku 9x9

Đồ án Giải thuật và Lập trình

Trong mảng hai chiều đưa vào chương trình, các phần tử trong mảng tương ứng
với các ô trong đề Sudoku. Trong mảng này, các ô chưa được điền nhận giá trị bằng 0,
các ô đã được điền mang giá trị đã được điền vào. Với mỗi ô chưa được điền, giá trị của ô
đó bằng 0.
Khi đó để kiểm tra khả năng điền giá trị của ô đó ta dùng hàm check() như sau:
-

Kiểm tra hàng: Kiểm tra giá trị k (với k có thể nhận giá trị từ 1-9) có thể điền vào
ô trên hay không. Kiểm tra xem trên hàng đó đã có ô nào có giá trị bằng k hay
chưa bằng cách duyệt hết tất cả các phần tử trên hàng đó, nếu đã có thì loại bỏ khả
năng ô đó nhận giá trị k .

-


Kiểm tra cột: Kiểm tra giá trị k (với k có thể nhận giá trị từ 1-9) có thể điền vào ô
đó không. Kiểm tra xem trên cột đó đã có ô nào mang giá trị k chưa bằng cách
duyệt hết tất cả các phần tử có trong cột, nếu đã có thì loại bỏ khả năng ô đó nhận
giá trị k.

-

Kiểm tra trong vùng 9 ô: với giá trị k có thể có giá trị từ 1 đến 9. Kiểm tra xem
trong vùng 9 ô có ô nào chứa giá trị k chưa, nếu có thì loại trừ khả năng nhận giá
trị k của ô đó.

-

Nếu với một số k sau khi kiểm tra đạt cả 3 khả năng trên thì có thể gán giá trị k
cho ô đó.
Trong đó các tham số truyền vào là:
+ Mảng S[][] là mảng chúng ta đang xét.
+ Số nguyên x chứa vị trí dòng của ô đang xét trong mảng.
+ Số nguyên y chứa vị trí cột của ô đang xét.
+ k là giá trị muốn kiểm tra xem có thể điền vào ô đang xét hay không.
Hàm trên sẽ trả về giá trị 1 nếu k được điền vào ô đang xét và trả về 0 nếu
không được điền vào ô đang xét.
Hàm sovle() thực hiện thuật toán quay lui. Hàm có 3 tham số đầu vào: mảng dữ liệu

S, biến vị trí x,y. Chương trình sẽ duyệt trên từng ô của mảng hai chiều, trên từng ô
chương trình sẽ kiểm tra xem ô đó đã được điền hay chưa, nếu chưa được điền thì
chương trình sẽ duyệt hết tất cả các giá trị k chạy từ 1- 9 xem những giá trị nào có thể
điền vào ô đang xét. Tại đây ứng với mỗi giá trị có thể điền vào ô đang xét chương trình
sẽ gọi đệ quy đến ô tiếp theo trong hàng. Nếu trường hợp do giá trị điền ô trước đó mà ô
tiếp theo không thể điền giá trị thì chương trình sẽ quay lui lại và thử với giá trị khác. Cứ

Trang 8


Đề tài: Giải Sudoku 9x9

Đồ án Giải thuật và Lập trình

như thế cho đến khi xét xong ô cuối cùng trong mảng (ô thứ 81). Tại đây chương trình sẽ
xuất ra kết quả và kết thúc chương trình.
3.3. Thuật toán
3.3.1. Thuật toán quay lui
Thuật toán quay lui (backtracking) như tên gọi của nó, là một quá trình tìm kiếm
mà trong quá trình tìm kiếm, nếu ta gặp một hướng lựa chọn không thỏa mãn, ta quay lui
về điểm lựa chọn nơi có các hướng khác và thừ hướng lựa chọn tiếp theo. Quá trình tìm
kiếm thất bại khi không còn điểm lựa chọn nào nữa.
Độ phức tạp:
Trong trường hợp xấu nhất độ phức tạp của quay lui vẫn là cấp số mũ. Vì nó mắc
phải các nhược điểm sau:


Rơi vào tình trạng "thrashing": quá trình tìm kiếm gặp phải bế tắc với cùng một
nguyên nhân.



Thực hiện các công việc dư thừa: Mỗi lần chúng ta quay lui, chúng ta cần phải
đánh giá lại lời giải trong khi đôi lúc điều đó không cần thiết.




Không sớm phát hiện được các khả năng bị bế tắc trong tương lai. Quay lui chuẩn,
không có cơ chế nhìn về tương lai để nhận biết được nhánh tìm kiếm sẽ đi vào bế
tắc.

4. CHƯƠNG TRÌNH VÀ KẾT QUẢ
4.1. Tổ chức chương trình
#include<time.h>
#include<stdio.h>
#include<windows.h>
int c=0;
char N[10] = {'_', '1', '2','3', '4', '5', '6', '7', '8', '9'};

Trang 9


Đề tài: Giải Sudoku 9x9

Đồ án Giải thuật và Lập trình

int S[9][9];
void show(int S[][9]);
void gotoxy(int x, int y);
void setcolor(int color);
void tientrinh(int i, int j, int k,int color);
int readFile(FILE *f);
void getData(int S[][9]);
void option1_solve_sudoku(int S[9][9], int x, int y);
void option2_solve_sudoku(int S[9][9], int x, int y);
int check(int S[][9], int x, int y, int k);
void Output(int S[][9]);

void resuilt(int S[][9]);
void main(void){
getData(S);
int index;
int t=1;
show(S);
printf("\n\n");
while (t)
{
printf("-------------SUDOKU---------------\n");
printf("1. Xem tien trinh :

\n");

printf("2. Xem dap an :

\n");

printf("----------------------------------\n");
printf("Nhap lua chon:");
scanf("%d",&index);
switch (index)
{
case 1:
option1_solve_sudoku(S,0,0);
break;
case 2:
option2_solve_sudoku(S,0,0);
c=0;


Trang 10


Đề tài: Giải Sudoku 9x9

Đồ án Giải thuật và Lập trình

break;
case 0:
t=0;
break;
default :
break;
}

}
}
// ham gotoxy
void gotoxy(int x, int y)
{
static HANDLE h = NULL;
if(!h)
h = GetStdHandle(STD_OUTPUT_HANDLE);
COORD c = { x, y };
SetConsoleCursorPosition(h,c);
}
// ham thiet lap mau chu
void setcolor(int color)
{
HANDLE hConsoleColor;

hConsoleColor = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleTextAttribute(hConsoleColor, color);
}
// ham tien trinh
void tientrinh(int i, int j, int k, int color)
{
int size= 4;
gotoxy(j*size, i*(size-2));
setcolor(color);
printf("%c", N[k]);

Trang 11


Đề tài: Giải Sudoku 9x9

Đồ án Giải thuật và Lập trình

}
// ham doc file
int readFile(FILE *f){
int n;
fscanf(f,"%d", &n);
return n;
}
// ham chuyen du lieu file qua mang S
void getData(int S[][9])
{
int A[81];
int count = 0;

FILE *f;
f = fopen("Input.txt", "r");
while (!feof(f)){
A[count] = readFile(f);
count++;
}
int t = 0;
int i, j;
for (i = 0; i < 9; i++)
{
for (j = 0; j < 9; j++)
{
S[i][j] = A[t];
t++;
}
}
fclose(f);
}
// lua chon tien trinh giai sudoku
void option1_solve_sudoku(int S[9][9], int x, int y){
Sleep(400);
if(y == 9){
if(x == 8){

Trang 12


Đề tài: Giải Sudoku 9x9

Đồ án Giải thuật và Lập trình


setcolor(12);
gotoxy(60, c*20+1);
printf("DAP AN %d:\n\n",c+1);
resuilt(S);
system("pause");
Output(S);
c++;
} else {
option1_solve_sudoku(S, x+1,0);
}
} else if(S[x][y] == 0){
int k = 0;
for (k = 1; k <=9; k++){
if(check(S,x,y,k)){
tientrinh(x, y, k,12);
S[x][y] = k;
option1_solve_sudoku(S, x, y+1);
tientrinh(x, y, 0, 5);
S[x][y] = 0;
Sleep(400);
}
}
} else {
option1_solve_sudoku(S,x,y+1);
}
}
// lua chon xuat tat ca dap an ra man hinh
void option2_solve_sudoku(int S[9][9], int x, int y){
if(y == 9){

if(x == 8){
setcolor(12);
gotoxy(60, c*20+1);
printf("DAP AN %d:\n\n",c+1);
resuilt(S);

Trang 13


Đề tài: Giải Sudoku 9x9

Đồ án Giải thuật và Lập trình

Output(S);
c++;
} else {
option2_solve_sudoku(S, x+1,0);
}
} else if(S[x][y] == 0){
int k = 0;
for (k = 1; k <=9; k++){
if(check(S,x,y,k)){
S[x][y] = k;
option2_solve_sudoku(S, x, y+1);
S[x][y] = 0;
}
}
} else {
option2_solve_sudoku(S,x,y+1);
}

}
// ham kiem tra so k co dien duoc vao vi tri x y hay ko
int check(int S[][9], int x, int y, int k){
int i = 0, j = 0;
for(i = 0; i <9 ; i++){
if(S[x][i] == k) return 0;
}
for(i = 0; i <9 ; i++){
if(S[i][y] == k) return 0;
}
int a = x/3, b = y/3;
for(i = 3*a; i < 3*a+3; i++){
for(j = 3*b; j < 3*b+3; j++){
if(S[i][j] == k) return 0;
}

Trang 14


Đề tài: Giải Sudoku 9x9

Đồ án Giải thuật và Lập trình

}
return 1;
}
// ham xuat mang
void show(int S[][9]){
setcolor(10);
int i = 0, j = 0;

int size= 4;
for ( i= 0; i<9 ; i++)
{
for (j= 0; j<9; j++)
{
gotoxy(j*size,i*(size-2));
printf("%c",N[S[i][j]]);
}
}
}
//ham xuat dap an ra man hinh
void resuilt(int S[][9]){
setcolor(3);
int i = 0, j = 0;
int size= 4;
for ( i= 0; i<9 ; i++)
{
for (j= 0; j<9; j++)
{
gotoxy(j*size+60,i*(size-2)+3+c*20);
printf("%c",N[S[i][j]]);
}
}
gotoxy(0, 21);
printf("\n");
}
// ham xuat ra file dap an

Trang 15



Đề tài: Giải Sudoku 9x9

Đồ án Giải thuật và Lập trình

void Output(int S[][9]){
FILE *p = fopen("Output.TXT", "w+");
int i, j;
for (i = 0; i < 9; i++)
{
for (j = 0; j < 9; j++)
{
int a = S[i][j];
fprintf(p, "%2d", S[i][j]);
}
fprintf(p, "\n");
}
fclose(p);
system("pause");
}

4.2. Ngôn ngữ cài đặt
Chương trình sử dụng ngôn ngữ C
4.3. Kết quả
4.3.1. Giao diện chính của chương trình
Đây là màn hình hiển thị các lựa chọn khi chạy chương trình sudoku 9x9
1. Lựa chọn 1: Xem các bước giải sudoku
2. Lựa chọn 2: Xem đáp án

Trang 16



Đề tài: Giải Sudoku 9x9

Đồ án Giải thuật và Lập trình

Hình 5. Giao diện chương trình chính
4.3.2.

Kết quả thực thi của chương trình
Hình 6. Đầu vào Input.txt

Trang 17


Đề tài: Giải Sudoku 9x9

Đồ án Giải thuật và Lập trình

Hình 7. Lựa chọn 1: Giải từng bước – Xem tiến trình bước 1 – Đáp án 1

Hình 8. Lựa chọn 1: Giải từng bước - Xem tiến trình bước 2 – Đáp án 2

Trang 18


Đề tài: Giải Sudoku 9x9

Đồ án Giải thuật và Lập trình


Hình 9. Lựa chọn 2: Xem đáp án 1

Hình 10. Lựa chọn 2: Xem đáp án 2

Trang 19


Đề tài: Giải Sudoku 9x9

Đồ án Giải thuật và Lập trình

Hình 11. So sánh kết quả và đầu ra output
4.3.3. Nhận xét
Chương trình giải được ô số sudoku có kích thước 9x9 có mức độ từ dễ đến khó. Hạn
chế của chương trình là chỉ đưa ra được một đáp án và nếu dữ liệu đầu vào quá ít thì
chương trình có thề không đưa ra được kết quả. Bài toán sudoku là bài toán phức tạp nên
nhóm chỉ thao tác với chế độ văn bản.

5. KẾT LUẬN VÀ HƯỚNG PHÁT TRIỂN
5.1. Kết luận
-

Ưu điểm:
+ Cài đặt được thuật toán quay lui để giải bài toán.
+ Chương trình đạt được yêu cầu đề ra (giải bài toán sudoku 9x9).
Nhược điểm:
+ Chỉ giải được Sudoku 9x9, chưa mở rộng được bài toán với các dạng các
nhau.

5.2. Hướng phát triển

Sudoku ngày nay có rất nhiều biến thể, vì vậy cần phải phát triển chương trình theo
hướng có thể giải nhiều dạng sudoku khác nhau, áp dụng nhiều thuật toán nhằm giải tối
ưu hơn.

Trang 20


Đề tài: Giải Sudoku 9x9

Đồ án Giải thuật và Lập trình

TÀI LIỆU THAM KHẢO
1. Sudoku < />2. Thuật toán quay lui (Backtracking)
<a/p/thuat-toan-quay-lui-backtracking-bJzKmLbD59N>
3. Đồ án cấu trúc dữ liệu và giải thuật (Giải sudoku)
< />
Trang 21



×