CHƯƠNG I: CƠ SỞ LÝ THUYẾT VỀ GIẢI THUẬT BACKTRACKING1.1 Khái niệm về giải thuật Backtracking Giải thuật Backtracking hay còn gọi là thuật toán quay luilà một kỹ thuật giảiquyết vấn đề,
CƠ SỞ LÝ THUYẾT VỀ GIẢI THUẬT BACKTRACKING
Khái niệm về giải thuật Backtracking
Giải thuật Backtracking (hay còn gọi là thuật toán quay lui)là một kỹ thuật giải quyết vấn đề, đặc biệt là các bài toán tìm kiếm, bằng cách xây dựng giải pháp từng bước một, và khi gặp phải vấn đề không khả thi, nó sẽ quay lại (quay lại) để thử một lựa chọn khác Thuật toán này thường được sử dụng khi cần duyệt qua nhiều cấu hình có thể, hoặc khi cần tìm tất cả các giải pháp thỏa mãn một điều kiện nào đó.
Đặc điểm và nguyên lý hoạt động của Backtracking
1.2.1 Đặc điểm của giải thuật Backtracking
Duyệt theo chiều sâu (DFS) trong không gian tìm kiếm.
Thử – sai – quay lui khi gặp lựa chọn không phù hợp.
Áp dụng đệ quy để xử lý từng bước lựa chọn.
Không lưu toàn bộ trạng thái, tiết kiệm bộ nhớ hơn so với quy hoạch động.
Loại bỏ sớm các nhánh không thỏa mãn điều kiện để tăng hiệu quả.
Có thể dừng sớm khi tìm được lời giải.
Thường dùng cho bài toán tổ hợp hoặc liệt kê các khả năng.
Dễ triển khai, dễ hiểu, nhưng hiệu suất thấp nếu không có ràng buộc tốt. 1.2.2 Nguyên lý hoạt động của giải thuật Backtracking
Nguyên tắc hoạt động của thuật toán quay lui (Backtracking) dựa trên phương pháp thử và sai kết hợp với việc quay lại để kiểm tra tất cả các khả năng lựa chọn có thể xảy ra Thuật toán sẽ liên tục tìm kiếm và mở rộng các lựa chọn cho đến khi tìm ra kết quả mong muốn hoặc xác định rằng không còn khả năng nào phù hợp Phương pháp này giúp tối ưu hóa quá trình tìm kiếm và đảm bảo mọi khả năng đều được xem xét kỹ lưỡng.
Khi thử một khả năng lựa chọn, thuật toán sẽ kiểm tra xem lựa chọn đó có thỏa mãn các ràng buộc hay không Nếu lựa chọn phù hợp, thuật toán sẽ tiếp tục tiến sâu vào các bước tiếp theo của bài toán tối ưu hóa Ngược lại, nếu lựa chọn không hợp lệ, thuật toán sẽ quay lui để tìm ra giải pháp tối ưu nhất.
(backtrack) và thử các lựa chọn khác.
So sánh Backtracking với các phương pháp tìm kiếm khác (Brute Force, DFS,
Cả hai đều đi theo chiều sâu, nghĩa là đi càng sâu càng tốt trước khi quay lại.
Thường dùng đệ quy để triển khai.
Dùng trong các bài toán tổ hợp, cây, hoặc duyệt đồ thị.
DFS thường chỉ dùng để duyệt qua các đỉnh, không kiểm tra điều kiện ràng buộc tại mỗi bước còn Backtracking thì kiểm tra kỹ từng bước đi và quay lui nếu thấy không hợp lệ.
DFS không cần phải lưu trạng thái hay hủy các bước đã đi, còn Backtracking thì cần trả lại trạng thái trước đó để thử hướng khác.
Backtracking giúp tìm tất cả lời giải đúng hoặc tốt nhất cho bài toán, trong khi đó DFS thường chỉ tìm được một đường đi cụ thể mà không xem xét tính đúng/sai toàn cục của lời giải Phương pháp backtracking phù hợp để giải các bài toán yêu cầu tìm tất cả các phương án khả thi, còn DFS phù hợp cho các bài toán đơn giản hơn hoặc khi chỉ cần một lời giải Việc lựa chọn giữa backtracking và DFS phụ thuộc vào mục tiêu và đặc điểm của bài toán cần giải quyết.
Cả hai đều áp dụng trong các bài toán tìm lời giải, như tối ưu hoặc chọn lựa tổ hợp.
Đều có thể làm việc với nhiều lựa chọn tại mỗi bước.
Greedy thường chạy rất nhanh nhưng có thể cho kết quả sai hoặc không tối ưu, trong khi Backtracking chậm hơn nhưng có khả năng tìm ra lời giải tối ưu hoặc chính xác nhất nếu được kiểm soát tốt.
Các bài toán kinh điển sử dụng Backtracking
Thuật toán backtracking là phương pháp giải quyết các bài toán tổ hợp, tìm kiếm và liệt kê bằng cách thử từng lựa chọn và loại bỏ những phương án không phù hợp Nó thường được sử dụng để giải các bài toán hoán vị, bài toán điền giá trị theo ràng buộc như Sudoku, tô màu đồ thị, N-Queen, cũng như trong các bài toán tìm đường đi, sắp xếp hoặc xếp hình Thuật toán này giúp tối ưu quá trình tìm kiếm lời giải đúng đắn, hiệu quả cho các bài toán phức tạp và có tính ràng buộc cao.
Một số bài toán kinh điểm của giải thuật Backtracking:
Hình 1.4.1 Áp dụng giải thuật Backtracking vào bài toán Sudoku
Bài toán Tô màu đồ thị
Hình 1.4.2 Áp dụng giải thuật Backtracking vào bài toán Tô màu đồ thị
Hình 1.4.3 Áp dụng giải thuật Backtracking vào bài toán N-Queen
Ưu và nhược điểm của giải thuật Backtracking
1.5.1 Ưu điểm của giải thuật Backtracking
Tổng quát và linh hoạt:Áp dụng được cho nhiều loại bài toán như: sắp xếp, tổ hợp, tìm đường, tô màu đồ thị, sudoku, bài toán 8 quân hậu, knapsack, v.v.
Đảm bảo tìm được lời giải (nếu có):Vì duyệt toàn bộ không gian nghiệm nên thuật toán sẽ tìm ra nghiệm đúng nếu tồn tại.
Dễ hiện thực với đệ quy:Dễ cài đặt bằng ngôn ngữ lập trình hỗ trợ đệ quy (như
Tiết kiệm bộ nhớ hơn quy hoạch động:Không cần bảng lưu trữ lớn như
Có thể kết hợp với ràng buộc để cắt tỉa nhanh hơn:Các ràng buộc (constraint) giúp bỏ qua các nhánh không cần thiết, tiết kiệm thời gian.
1.5.2 Nhược điểm của giải thuật Backtracking
Chi phí tính toán cao trong trường hợp xấu:Trong trường hợp tệ nhất, phải duyệt toàn bộ không gian nghiệm (có thể lên tới hàng triệu tổ hợp).
Không tối ưu cho các bài toán lớn:Khi số lượng lựa chọn tăng lên, thời gian chạy tăng theo cấp số mũ.
Không luôn tìm ra lời giải tối ưu đầu tiên:Chỉ dừng lại khi gặp lời giải đầu tiên
(nếu không kết hợp thêm chiến lược tối ưu hóa).
Cần hiểu rõ điều kiện ràng buộc để cắt tỉa hiệu quả:Nếu không có điều kiện kiểm tra sớm (pruning) hợp lý thì thuật toán chạy chậm.
Ứng dụng thực tế của giải thuật Backtracking
1.6.1 Ứng dụng giải thuật Backtracking để tìm kiếm và kiệt kê các bài toán
Tính năng quay lui là công cụ hiệu quả để tìm đường đi trong các mê cung, sơ đồ hoặc bảng tính phù hợp Thuật toán này thử nhiều hướng đi khác nhau, tự động "quay lui" khi đường đi không dẫn đến đích hoặc không phù hợp với quy tắc, giúp xác định con đường tối ưu một cách nhanh chóng và chính xác.
Liệt kê các cấu hình:
Backtracking có thể liệt kê tất cả các cấu hình có thể của một tập hợp, ví dụ như liệt kê tất cả các vị trí, tổ hợp hoặc nhị phân chuỗi có định nghĩa dài nhất
Giải thích Sudoku và các câu đố logic:
Quay lui là một phương pháp hiệu quả để giải quyết các câu hỏi logic như Sudoku, Minesweeper và các câu hỏi tìm đường khác
Tìm kiếm các từ khóa:
Trong bài toán tìm kiếm con, việc quay lui có thể được sử dụng để tìm kiếm các điều kiện thỏa mãn một điều kiện nhất định trong một chuỗi lớn hơn
Bài toán mã hóa và giải mã:
Tính năng quay lui là công cụ hiệu quả để giải mã các thông điệp được mã hóa bằng Morse mã hóa, đặc biệt trong các trường hợp cần xử lý ký tự có độ dài biến đổi Phương pháp này giúp người dùng dễ dàng phục hồi nội dung gốc của các tin nhắn đã được mã hóa, nâng cao khả năng giải mã trong các quá trình truyền thông bảo mật Sử dụng chức năng quay lui không những tối ưu hóa quá trình giải mã, mà còn đảm bảo chính xác khi xử lý các ký tự mã hóa có độ dài thay đổi, phù hợp với các hệ thống mã hóa phức tạp.
1.6.2 Ứng dụng giải thuật Backtracking vào các bài toán tối ưu hóa
Bài toán xếp hậu cần:
Bài toán N hậu quân trên bàn cờ là ví dụ điển hình về việc áp dụng thuật toán quay lui để tìm kiếm cấu hình tối ưu Mục tiêu của bài toán là đặt N quân hậu sao cho không quân hậu nào tấn công lẫn nhau, đảm bảo tính khả thi của mỗi bố trí Phương pháp quay lui giúp duyệt tất cả các tổ hợp có thể, từ đó xác định cấu hình phù hợp nhất đáp ứng yêu cầu không xung đột Đây là một ví dụ nổi bật minh chứng cho khả năng của thuật toán quay lui trong các bài toán tối ưu hóa tổ hợp trên bảng cờ vua.
Đường tuyến tính ưu tiên tối ưu:
Trong các bài toán tối ưu hóa tuyến tính, tính năng quay lui đóng vai trò quan trọng trong việc xác định các đường dẫn tối ưu Phương pháp này giúp tìm ra các tuyến đường ngắn nhất hoặc nhanh nhất, từ đó nâng cao hiệu quả giải quyết các bài toán về đường đi Quay lui còn có khả năng lựa chọn các đường dẫn tối ưu nhất dựa trên các tiêu chí khác nhau, tối ưu hóa quá trình tìm kiếm và đảm bảo kết quả chính xác.
Trong chương I, chúng ta đã tìm hiểu tổng quan về giải thuật Backtracking – một phương pháp tìm kiếm theo chiều sâu có kiểm soát, đóng vai trò quan trọng trong việc giải quyết các bài toán tổ hợp và ràng buộc Qua việc phân tích nguyên lý hoạt động, so sánh với các kỹ thuật tìm kiếm khác như Brute Force hay DFS, có thể thấy Backtracking nổi bật nhờ khả năng loại bỏ sớm các nhánh không tiềm năng, từ đó giảm đáng kể chi phí tính toán Các bài toán kinh điển như Sudoku, N quân hậu, phân hoạch chuỗi và tô màu đồ thị minh chứng rõ nét cho hiệu quả thực tiễn của thuật toán này Dù vẫn tồn tại một số hạn chế như thời gian xử lý cao trong bài toán lớn, nhưng với sự kết hợp cùng các chiến lược tối ưu khác, Backtracking vẫn là một công cụ mạnh mẽ và linh hoạt trong lĩnh vực thuật toán Việc hiểu sâu về giải thuật này không chỉ giúp nâng cao tư duy thuật toán mà còn mở rộng khả năng ứng dụng trong nhiều lĩnh vực như trí tuệ nhân tạo, lập lịch, sinh học tính toán và thiết kế hệ thống.
ỨNG DỤNG GIẢI THUẬT BACKTRACKING VÀO BÀI TOÁN N- QUEEN
Phân tích độ phức tạp và đánh giá hiệu suất của bài toán N-Queen
2.4.1 Độ phức tạp của thuật toán Backtraking vào bài toán N-Queen
Bài toán đặt N quân hậu trên bàn cờ N×N sao cho không quân nào tấn công nhau.
Mỗi hàng có thể đặt quân hậu tại N vị trí → N lựa chọn ở mỗi bước.
Tuy nhiên, không phải vị trí nào cũng hợp lệ do bị ràng buộc bởi cột và đường chéo.
Trong trường hợp xấu nhất (không cắt tỉa được), độ phức tạp tiệm cận là:
Vì mỗi hàng chỉ đặt 1 hậu, và ta phải thử tất cả cách đặt không trùng cột.
2.4.2 Đánh giá hiệu xuất của bài toán N-Queen khi áp dụng giải thuật Backtracking
Thuật toán Backtracking được sử dụng phổ biến để giải bài toán N-Queen nhờ tính đơn giản, dễ cài đặt và khả năng tìm ra tất cả các nghiệm hợp lệ Tuy nhiên, hiệu suất của giải thuật này phụ thuộc vào kích thước của bàn cờ (N) và phương pháp kiểm tra ràng buộc trong quá trình quay lui, ảnh hưởng đến tốc độ và hiệu quả giải quyết bài toán.
2.4.2.1 Ưu điểm về hiệu xuất
Hiệu quả khi N nhỏ đến vừa (N ≤ 12):
Với N = 8, chương trình tìm ra tất cả 92 nghiệm gần như ngay lập tức.
Các nghiệm được lưu trong bộ nhớ và hiển thị tuần tự qua giao diện người dùng (GUI).
Tối ưu cắt tỉa hiệu quả nhờ kiểm tra điều kiện “an toàn”:
Việc kiểm tra trùng cột và đường chéo trong hàm an_toan() giúp loại bỏ hàng triệu cấu hình sai.
Tốc độ quay lui cải thiện đáng kể so với thuật toán duyệt tổ hợp toàn bộ.
Bộ nhớ sử dụng hợp lý:
Danh sách nghiệm self.danh_sach_nghiem lưu trữ toàn bộ kết quả.
Với N ≤ 10, số nghiệm vẫn ở mức thấp nên không gây áp lực lên bộ nhớ.
Tốc độ hiển thị tốt với GUI Tkinter:
Giao diện được cập nhật mượt mà.
Người dùng có thể dễ dàng xem từng nghiệm bằng nút “Trước” và “Tiếp”.
2.4.2.2 Hạn chế về hiệu suất
Tăng độ phức tạp khi N lớn:
Khi N > 12, số lượng cấu hình cần thử tăng mạnh, khiến thời gian xử lý dài hơn đáng kể.
Với N = 15, có thể mất vài giây đến hàng chục giây để tìm ra tất cả nghiệm (tuỳ cấu hình máy tính).
Không dùng kỹ thuật tối ưu nâng cao:
Chương trình chỉ dùng Backtracking đơn thuần, chưa áp dụng Bitmask,
Branch and Bound hay Symmetry Breaking để rút ngắn thời gian. cho N lớn hơn, cần tích hợp thêm các kỹ thuật tối ưu khác để đảm bảo hiệu suất và tiết kiệm tài nguyên.
ỨNG DỤNG GIẢI THUẬT BACKTRACKING VÀO BÀI TOÁN SUDOKU
Giới thiệu bài toán Sudoku
Hình 3.1.1 Một bảng Sudoku điển hình
Sudoku là trò chơi giải đố phổ biến toàn cầu, thu hút người chơi bởi tính thử thách và logic cao Một bảng Sudoku tiêu chuẩn có dạng lưới vuông 9x9, được chia thành 9 ô con kích thước 3x3, tạo thành cấu trúc logic để điền số Nhiệm vụ của người chơi là điền các số từ 1 đến 9 vào các ô trống sao cho mỗi hàng, mỗi cột và mỗi ô con đều chứa đầy đủ các số từ 1 đến 9 mà không lặp lại Trò chơi đòi hỏi tư duy phản biện, khả năng lập kế hoạch và tập trung cao độ nhằm hoàn thành bản thiết kế một cách chính xác và hợp lý Sudoku không chỉ giúp rèn luyện trí não mà còn mang lại cảm giác thú vị, thử thách trí tuệ cho mọi lứa tuổi.
Mỗi hàng chứa đủ các số từ 1 đến 9, không lặp lại.
Mỗi cột chứa đủ các số từ 1 đến 9, không lặp lại.
Mỗi ô con 3*3 cũng chứa đủ các số từ 1 đến 9, không lặp lại.
Hình 3.1.2 Đáp án của hình 3.1.1
Sudoku không chỉ mang tính giải trí mà còn rèn luyện tư duy logic, khả năng quan sát và tính kiên nhẫn Trong lĩnh vực Khoa học Máy tính, Sudoku còn là ví dụ kinh điển để minh họa các giải thuật tìm kiếm và ràng buộc
Tổ chức dữ liệu cho bài toán Sudoku
Ta minh họa quá trình tìm kiếm lời giải cho bài toán Sudoku với bảng kích thước 9×9. Mỗi trạng thái trong quá trình tìm kiếm tương ứng với một bảng Sudoku tại thời điểm có một số ô đã được điền, còn một số ô vẫn chưa xác định. Ở trạng thái ban đầu, ta thu thập dữ liệu từ người dùng nhập vào giao diện: các ô trống được gán giá trị 0, còn các ô đã điền hợp lệ (từ 1 đến 9) được giữ nguyên Dữ liệu được tổ chức dưới dạng một ma trận hai chiều 9x9, với mỗi phần tử đại diện cho giá trị tại một ô trên bảng.
Thuật toán backtracking bắt đầu quét qua từng ô theo chiều từ trái sang phải, từ trên xuống dưới, để tìm các ô trống trên bảng Khi gặp ô trống, thuật toán thử lần lượt điền các giá trị từ 1 đến 9 vào ô đó Đối với mỗi giá trị được thử, thuật toán kiểm tra xem giá trị đó có thỏa mãn ba điều kiện ràng buộc của Sudoku hay không, nhằm đảm bảo tính hợp lệ của lời giải Quá trình này lặp lại đệ quy cho đến khi toàn bộ bảng được điền đúng các số phù hợp hoặc xác định rằng không có lời giải khả thi.
Không trùng số trên cùng một hàng
Không trùng số trên cùng một cột
Không trùng số trong cùng một khối 3x3
Nếu số đó hợp lệ, nó được điền tạm thời vào ô, và thuật toán tiếp tục xét đến ô tiếp theo (đệ quy) Nếu tại bất kỳ thời điểm nào không thể gán giá trị hợp lệ cho một ô, thuật toán quay lui (backtrack) để thử lại số khác ở bước trước.
Quá trình giải thuật toán Backtracking:
Ví dụ, nếu tại ô (1,1), ta thử số 5 là hợp lệ, thì ta đi xuống ô (1,1)=5 để tiếp tục quá trình thử giá trị Từ trạng thái này, ta tiếp tục thử các giá trị cho ô tiếp theo như (1,2), tạo ra các nhánh mới dựa trên các giá trị hợp lệ Quá trình này lặp lại liên tục cho đến khi hoàn thành việc điền đầy đủ các ô trong bảng, đảm bảo giải pháp hợp lệ.
Tất cả các ô đã được điền giá trị hợp lệ thì thu được một lời giải hoàn chỉnh
Hoặc không còn ô nào hợp lệ thì thuật toán quay lui để thử ô khác
Nếu có trùng số trong một khối 3x3 thì sẽ báo “ không tìm được lời giải”
Trong giao diện phần mềm, các ô trống ban đầu thể hiện vị trí cần giải của Sudoku, trong khi các ô có nền xanh nhạt đã chứa giá trị do người dùng nhập Khi nhấn nút "Giải Sudoku", hệ thống sẽ tự động xử lý và hiển thị kết quả trực tiếp trên giao diện người dùng, giúp người chơi dễ dàng theo dõi quá trình giải và kết quả cuối cùng.
Sau đây là hỉnh ảnh minh họa và ví dụ :
Hình 3.2.1 Ví dụ về bài toán Sudoku
Start :bắt đầu từ ô (0,2) với các giá trị thử 1, 2, 3.
Mỗi nhánh tiếp tục đến ô (0,3) với các giá trị 4, 5.
Tiếp tục đến lời giải thành công (Solved) nếu hợp lệ ( nhánh (0,2)=1 suy ra (0,3)=5).
Hoặc Backtrack nếu không hợp lệ.
Cài đặt giải thuật Backtracking cho bài toán Sudoku
3.3.1 Các bước thực hiện giải thuật Backtracking vào bài toán Sudoku
Bước 1: Tìm ô trống đầu tiên
Duyệt toàn bộ bảng Sudoku (thường là 9x9) để tìm ô có giá trị bằng 0 (hoặc ký hiệu đặc biệt, ví dụ: nếu là ký tự).
Nếu không còn ô trống => bài toán đã hoàn thành (trả về thành công).
Bước 2: Thử tất cả các giá trị hợp lệ (1 đến 9)
Trong quá trình kiểm tra hợp lệ của các số từ 1 đến 9 trong ô đang xét, cần đảm bảo rằng số đó chưa xuất hiện trong hàng hiện tại, chưa xuất hiện trong cột hiện tại và chưa xuất hiện trong ô vuông nhỏ 3x3 chứa ô đó, giúp xác định chính xác các số phù hợp để điền vào ô một cách hợp lệ trong trò chơi Sudoku.
Bước 3: Gán giá trị và gọi đệ quy
Nếu số hợp lệ: o Gán số đó vào ô trống. o Gọi đệ quy để giải phần còn lại của bảng.
Bước 4: Nếu không giải được, quay lui (backtrack)
Nếu sau khi thử giá trị hiện tại không tìm ra lời giải, bạn nên xóa giá trị đã gán bằng cách đặt lại về 0 hoặc ký hiệu trống ban đầu và tiếp tục thử các số tiếp theo để tìm ra nghiệm chính xác.
Bước 5: Trả về kết quả
Nếu tất cả các ô đều được điền đúng => thành công.
Nếu thử hết mà không tìm được lời giải phù hợp => không có nghiệm.
3.3.2 Phân tích chi tiết giải thuật Backtracking vào bài toán Sudoku
Khởi tạo hàm ( constructor) def init (self, cua_so): self.cua_so = cua_so self.o = [[None for _ in range(9)] for _ in range(9)] self.tao_giao_dien()
Lưu của sổ Tkinter vào biến self.cua_so
- Cua_so là đối tượng Tk() truyền vào khi tạo app dùng để sử dụng các hàm khác như title(), geometry(), grid(), …
Tạo ma trận 9x9 chứa các giá trị là None trong mỗi ô
Gọi hàm tao_dao_dien()
Hàm này tạo giao diện cho game bằng thư viện Tkinter
Bạn có thể đặt tên cho giao diện trò chơi bằng cách sử dụng phương thức `self.cua_so.title("Sudoku Game")`, giúp người chơi dễ nhận biết Kích thước của cửa sổ được thiết lập là 580x480 pixels thông qua lệnh `self.cua_so.geometry("580x480")`, đảm bảo giao diện hiển thị rõ ràng và phù hợp trên các thiết bị khác nhau Bạn cũng có thể thêm biểu tượng cho cửa sổ bằng cách tải hình ảnh từ tệp 'sudoku.png' với `PhotoImage(file='sudoku.png')`, và gán nó làm icon của cửa sổ bằng `self.cua_so.iconphoto(True, icon)`, nhưng cần xử lý trường hợp tệp hình ảnh không tồn tại để tránh lỗi.
Create a 9x9 matrix and assign Entry widgets to it within the initialization function Loop through each row and column to generate input fields, customizing their appearance with specific width, font, and centered text Apply a checkered pattern by setting the background color for alternate cells to enhance visual clarity Store each Entry widget into the self.o matrix for easy access and management.
Tạo nút giải , nút tạo đề , nút gợi ý nut_giai = tk.Button(self.cua_so, text='Giải', command=self.giai_sudoku_gui, font=('Arial', 20), bg='#81c784') nut_giai.grid(row=9, column=0, columnspan=9, pady) nut_tao_de = tk.Button(self.cua_so, text='Tạo đề', command=self.tao_de_ngau_nhien, font=('Arial', 20), bg='#81c784') nut_tao_de.grid(row=9, column=0, columnspan=4, pady) nut_goi_y = tk.Button(self.cua_so, text='Gợi ý', command=self.goi_y_mot_o, font=('Arial', 16), bg='#ffd54f') nut_goi_y.grid(row=9, column=0, columnspan=1, pady)
Hàm này dùng để lấy dữ liệu từ người dùng
Bước 1: Tạo bảng và sử dụng get() để lấy dữ liệu từ người dung. for i in range(9): dong = [] for j in range(9): gia_tri = self.o[i][j].get()
Bước 2: Nếu giá trị ở ô bất kỳ không có số thì sẽ được mặt định là 0 và ngược lại nếu trong ô có giá trị từ 1 -> 9 thì sẽ lấy số đó ( nhớ chuyển kiểu dữ liệu int cho số ) và trả lại bảng if gia_tri == "": dong.append(0) else: try: so = int(gia_tri) if 1