Các bài toán liệt kê có thể có nhiều dạng, từ việc liệt kê các hoán vị của một dãy số, tổ hợp của các phần tử trong một tập hợp, đến các vấn đề phức tạp hơn như liệt kê các cấu trúc đồ t
Trang 1HỌC VIỆN CÔNG NGHỆ BƯU CHÍNH VIỄN THÔNG
- -
BÀI TẬP LỚN MÔN: TOÁN RỜI RẠC NHÓM MÔN HỌC: 01
Trang 2ĐỀ BÀI: Tìm hiểu bài toán liệt kê và cách giải bài toán
Bảng phân công công việc
việc, tổng hợp nội dung, viết tiểu luận
dung, thuyết trình
Trang 3Mục Lục
I Giới thiệu và tìm hiểu chung về bài toán liệt kê 4
1 Giới thiệu 4
2 Mục tiêu của bài toán liệt kê 5
3 Tầm quan trọng của bài toán liệt kê 6
4 Các phương pháp giải bài toán liệt kê 6
5 Phân tích độ phức tạp thuật toán 7
6 Một số dạng toán điển hình của bài toán liệt kê: 7
II Tìm hiểu chi tiết về phương pháp sinh 9
1 Nguyên lý 9
2 Ưu điểm 10
3 Nhược điểm 11
III Tìm hiểu chi tiết về phương pháp quay lui 11
1 Nguyên lý 11
2 Ưu điểm 13
3 Nhược điểm 14
IV Một số ví dụ minh họa thuật toán sinh 14
a) Sinh xâu nhị phân có độ dài N 14
b) Bài toán sinh tổ hợp chập k của N phần tử 17
V Ví Dụ Phương Pháp quay lui: 19
a) Sinh xâu nhị phân có độ dài N 19
b) Tổ hợp chập K của N 20
c) Quay lui hoán vị của N phần tử 20
d) Bài toán N-queen (bài toán kinh điển của thuật toán quay lui) 22
VI Tổng Kết 25
1 So sánh giữa phương pháp sinh và quay lui 25
2 Ứng dụng thực tiễn của bài toán liệt kê 25
3 Hướng phát triển của bài toán liệt kê 26
4 Một số tài liệu tham khảo về bài toán liệt kê 26
5 Kết luận 28
Trang 4Leonhard Euler (1707–1783): Ông là một trong những nhà toán học đầu
tiên đưa ra các lý thuyết tổ hợp, mà bài toán liệt kê là một phần quan trọng Euler đã giải quyết các bài toán liệt kê thông qua lý thuyết đồ thị, trở thành nền tảng cho lý thuyết đồ thị hiện đại
Claude Shannon (1916–2001): Claude Shannon được xem là "cha đẻ
của lý thuyết thông tin", một lĩnh vực có sự kết nối mạnh mẽ với bài toán liệt kê trong việc giải quyết các vấn đề truyền thông và mã hóa Shannon đã nghiên cứu cách thức truyền và lưu trữ thông tin hiệu quả, và điều này bao gồm cả việc liệt kê các mã và ký tự trong các hệ thống mã hóa Khả năng liệt kê tất cả các
mã có thể giúp đảm bảo an toàn và hiệu quả trong truyền thông Đóng góp của ông về lý thuyết mã hóa và thuật toán nén thông tin là một trong những ví dụ nổi bật về việc áp dụng bài toán liệt kê trong thế giới hiện đại
Trang 5Donald Knuth (sinh năm 1938): Donald Knuth là một nhà khoa học
máy tính lừng danh và là tác giả của bộ sách The Art of Computer
Programming, một tác phẩm kinh điển trong lĩnh vực thuật toán và cấu trúc dữ liệu Ông đã phát triển các thuật toán hiệu quả để sinh ra hoán vị, tổ hợp và các cấu trúc tổ hợp khác Những công trình của Knuth không chỉ giúp tối ưu hóa các thuật toán liệt kê mà còn mở ra những ứng dụng rộng rãi trong lập trình và
giải thuật hiện đại
Các bài toán liệt kê có thể có nhiều dạng, từ việc liệt kê các hoán vị của một dãy số, tổ hợp của các phần tử trong một tập hợp, đến các vấn đề phức tạp hơn như liệt kê các cấu trúc đồ thị hoặc chuỗi ký tự,
2 Mục tiêu của bài toán liệt kê
Mục tiêu chính của bài toán liệt kê là tạo ra tất cả các kết quả hợp lệ từ một tập hợp dữ liệu cho trước Khác với bài toán tổ hợp yêu cầu liệt kê tất cả các tập con có thể có từ một tập hợp các phần tử mà không quan tâm đến thứ tự của chúng.Bài toán liệt kê lại cần xác định một thuật toán để theo đó có thể xây dựng được lần lượt tất cả các cấu hình cần quan tâm Một thuật toán liệt kê phải đảm bảo hai nguyên tắc:
Trang 6• Không được lặp lại bất kỳ một cấu hình nào
• Không được bỏ sót bất kỳ một cấu hình nào
3 Tầm quan trọng của bài toán liệt kê
• Tìm kiếm toàn bộ giải pháp
Trong nhiều trường hợp, không chỉ việc tìm ra một giải pháp đúng là quan trọng, mà cần phải liệt kê tất cả các giải pháp có thể
• Phân tích và tối ưu
Thông qua liệt kê, chúng ta có thể phân tích toàn bộ không gian giải pháp để tìm ra giải pháp tối ưu hoặc có những đặc tính cụ thể
• Ứng dụng rộng rãi
Bài toán liệt kê có mặt trong nhiều bài toán khác nhau như giải quyết bài toán tối ưu hóa, mật mã, lập thời gian biểu, tổ hợp, và nhiều ứng dụng trong trí tuệ nhân tạo
Bài toán liệt kê có nhiều ứng dụng quan trọng thực tiễn: tối ưu hóa trong sản xuất và logistics, phân tích và xử lý dữ liệu lớn (Big Data), mật mã và bảo mật, thiết kế và triển khai hệ thống mạng, ,tối ưu hóa tài nguyên trong hệ thống viễn thông, xác suất và thống kê, Khoa học máy tính và thuật toán,
4 Các phương pháp giải bài toán liệt kê
Có hai phương pháp chính thường được sử dụng để giải quyết các bài toán liệt kê:
• Phương Pháp Sinh (Generation):
Phương pháp sinh là một kỹ thuật cơ bản nhưng mạnh mẽ, cho phép chúng
ta tạo ra từng phần tử của không gian tìm kiếm một cách tuần tự Phương pháp
Trang 7thái một cách có hệ thống mà không cần kiểm tra lại các trạng thái đã sinh ra trước đó
• Phương Pháp Quay Lui (Backtracking):
Phương pháp quay lui, ngược lại, là một chiến lược đệ quy linh hoạt, cho phép chúng ta khám phá không gian trạng thái bằng cách thử các lựa chọn khác nhau và quay lại (backtrack) khi phát hiện lựa chọn sai Đây là phương pháp đặc biệt hiệu quả khi không gian tìm kiếm quá lớn và việc kiểm tra từng khả năng là không khả thi
5 Phân tích độ phức tạp thuật toán
Độ phức tạp của các phương pháp liệt kê thường được tính toán dựa trên số lượng phần tử có thể sinh ra và các thao tác cần thiết để sinh ra mỗi phần tử Cả phương pháp sinh và quay lui đều có độ phức tạp cao, vì mục tiêu của chúng là liệt kê tất cả các khả năng có thể có
Phương pháp sinh thường có độ phức tạp là O(n!) cho các bài toán hoán vị
(với n là số lượng phần tử) hoặc O(2^n) cho các bài toán tổ hợp hoặc tập con
Đây là vì số lượng phần tử cần liệt kê tăng theo cấp số nhân khi số lượng phần
tử ban đầu tăng lên Phương pháp này có thể nhanh hơn trong một số trường hợp, nhưng không phải lúc nào cũng hiệu quả với các bài toán lớn, do số lượng phần tử cần liệt kê quá lớn
Phương pháp quay lui có độ phức tạp tương tự tùy vào bài toán cụ thể,
thường là O(n!) cho bài toán hoán vị và O(2^n) cho bài toán tổ hợp Tuy nhiên,
nhờ vào tính năng quay lui, phương pháp này có thể tránh được việc kiểm tra những trường hợp không khả thi, từ đó giúp giảm đáng kể số lượng thao tác cần thực hiện trong một số trường hợp Điều này giúp quay lui trở thành một
phương pháp ưu việt khi không gian tìm kiếm quá lớn và cần có cách tiếp cận khôn ngoan để giảm bớt các thao tác dư thừa
6 Một số dạng toán điển hình của bài toán liệt kê:
• Liệt kê tổ hợp
Xác định tất cả các tổ hợp có thể từ một tập hợp phần tử Tính số lượng tổ hợp của k phần tử từ n phần tử
• Liệt kê hoán vị
Xác định tất cả các hoán vị của một dãy số hoặc đối tượng
Trang 8• Liệt kê tập con
Xác định tất cả các tập con của một tập hợp cho trước
• Bài toán N-Queens
Bài toán này yêu cầu đặt N quân hậu trên bàn cờ N x N sao cho không quân hậu nào có thể tấn công nhau Sinh tất cả các cách đặt quân hậu hợp lệ, Tìm
số cách đặt quân hậu cho N
Trang 9II Tìm hiểu chi tiết về phương pháp sinh
1 Nguyên lý
Nguyên lý của phương pháp sinh là tạo ra tất cả các cấu hình hoặc tổ hợp
có thể từ một tập hợp các phần tử theo một thứ tự xác định Phương pháp này
dựa trên việc liệt kê tuần tự các phần tử trong không gian tìm kiếm mà không
cần kiểm tra lại các trạng thái đã sinh ra trước đó
Để liệt kê được hết các cấu hình trong một bài toán liệt kê sử dụng phương pháp sinh chúng ta cần biết :
• Cầu hình đầu tiên: Là điểm bắt đầu của quá trình tìm kiếm
• Cấu hình cuối cùng: Là điểm dừng, khi đã đạt được giải pháp hoàn chỉnh
• Phương pháp sinh: Các điều kiện để xác định tính hợp lệ của một cấu
hình
Phương pháp sinh thường được thực hiện thông qua một vòng lặp hoặc đệ quy Dưới đây là cách thức hoạt động chính:
Trang 101 Khởi tạo: Đặt các giá trị ban đầu cho các biến và điều kiện cần thiết
2 Tạo phần tử: Dựa trên các quy tắc đã định, sinh ra phần tử tiếp theo từ
tập hợp phần tử hiện tại
3 Kiểm tra và lưu trữ: Kiểm tra tính hợp lệ của phần tử đã sinh ra và lưu
trữ nếu nó đáp ứng điều kiện
4 Tiếp tục: Lặp lại quy trình cho đến khi cấu hình cuối cùng xuất hiện
Dưới đây là Mã giả cho phương pháp sinh:
<Đưa ra cấu hình đang có>;
if (cấu hình đang có chưa là cuối cùng) then <Sinh_kế_tiếp>
else Stop:= true;
Trang 11Hiệu quả với bài toán có không gian tìm kiếm rõ ràng: Phương pháp này thường được áp dụng cho các bài toán mà tất cả các tổ hợp đều có thể sinh ra dễ dàng và không yêu cầu kiểm tra tính hợp lệ
b) Dễ cài đặt
Các thuật toán sinh thường có cấu trúc lặp đơn giản hoặc đệ quy với số bước
rõ ràng, không yêu cầu quá nhiều điều kiện phức tạp hay kiểm tra nhiều lần trong quá trình sinh ra cấu hình
c) Sinh các cấu hình tuần tự
Phương pháp sinh thường sinh ra các cấu hình theo thứ tự, từ cấu hình nhỏ nhất đến lớn nhất Điều này giúp dễ dàng theo dõi quá trình sinh ra các kết quả
và có thể dừng ngay khi đạt được cấu hình mong muốn mà không phải sinh toàn
bộ
3 Nhược điểm
a) Không hiệu quả với các bài toán có ràng buộc phức tạp
Phương pháp sinh không phù hợp khi bài toán có nhiều điều kiện ràng buộc phức tạp hoặc yêu cầu phải kiểm tra tính hợp lệ của từng cấu hình
b) Tốn thời gian khi không gian tìm kiếm lớn, giảm hiệu suất
Trong các bài toán có không gian tìm kiếm lớn, phương pháp sinh có thể tốn rất nhiều thời gian để liệt kê toàn bộ các cấu hình Phương pháp sinh chỉ sinh ra cấu hình theo cách tuần tự mà không có cơ chế tối ưu hóa nào giúp giảm thiểu thời gian xử lý
c) Không tận dụng được tính chất của bài toán
Phương pháp sinh liệt kê tất cả các cấu hình mà không tận dụng được các tính chất đặc thù của bài toán để giảm không gian tìm kiếm Trong khi các phương pháp khác, như quay lui hoặc chia để trị, có thể loại bỏ nhiều khả năng không cần thiết trước khi sinh ra cấu hình
III Tìm hiểu chi tiết về phương pháp quay lui
1 Nguyên lý
Trang 12Nguyên lý của phương pháp quay lui là xây dựng giải pháp từng bước một và quay lại khi phát hiện rằng bước nào đó không dẫn đến một giải pháp hợp lệ Kỹ thuật này giúp tối ưu hóa quá trình tìm kiếm bằng cách loại bỏ những lựa chọn không khả thi, nhờ đó giảm số lượng khả năng cần kiểm tra
Để liệt kê được hết các cấu hình trong một bài toán liệt kê sử dụng phương pháp sinh chúng ta cần biết :
• Cầu hình đầu tiên: Là điểm bắt đầu của quá trình tìm kiếm
• Cấu hình cuối cùng: Là điểm dừng, khi đã đạt được giải pháp hoàn chỉnh
• Phương pháp quay lui: Các điều kiện để xác định tính hợp lệ của một cấu hình
Phương pháp quay lui thường được thực hiện thông qua một quy trình đệ quy hoặc vòng lặp Dưới đây là cách thức hoạt động chính:
1 Khởi tạo: Đặt các giá trị ban đầu cho các biến và điều kiện cần thiết
2 Lựa chọn: Chọn một phần tử từ các lựa chọn khả thi
3 Kiểm tra: Kiểm tra tính hợp lệ của phần tử đã chọn Nếu nó không hợp
lệ, quay lại bước trước đó và thử lựa chọn khác
4 Ghi nhận: Nếu phần tử hợp lệ, lưu trữ cấu hình và tiếp tục lựa chọn cho
bước tiếp theo
5 Tiếp tục: Lặp lại quy trình cho đến khi cấu hình cuối cùng xuất hiện
Dưới đây là Mã giả cho phương pháp sinh:
procedure Backtrack;
Trang 13Begin
<Xây dựng cấu hình đầu tiên>;
if (cấu hình đang có là hợp lệ) then Begin
Backtrack; // Gọi lại hàm đệ quy
<Quay lại>; // Thực hiện quay lui nếu cần
Trang 14Phương pháp quay lui giúp tiết kiệm tài nguyên tính toán bằng cách loại
bỏ những nhánh không khả thi ngay từ đầu, do đó không phải thử nghiệm tất cả
các khả năng
b) Linh hoạt
Quay lui có thể được áp dụng cho nhiều loại bài toán khác nhau, từ hoán vị,
tổ hợp đến các bài toán lập lịch và tìm kiếm đường đi, nhờ tính linh hoạt trong quy tắc kiểm tra và lựa chọn
b) Không tận dụng được tính chất của bài toán
Hiệu suất của phương pháp quay lui phụ thuộc vào cách kiểm tra tính hợp
lệ Nếu quy trình này mất nhiều thời gian, nó sẽ làm giảm hiệu suất tổng thể của thuật toán
c) Khó khăn trong việc tối ưu hóa
Mặc dù quay lui có thể loại bỏ nhiều lựa chọn không khả thi, nhưng trong một số trường hợp, việc tìm ra cách tốt nhất để tối ưu hóa và chọn lựa vẫn là một thách thức
IV Một số ví dụ minh họa thuật toán sinh
a) Sinh xâu nhị phân có độ dài N
Trang 15• Mục tiêu: Sinh tất cả các dãy nhị phân có độ dài n, tức là các dãy chỉ
chứa các phần tử 0 và 1
• Số lượng dãy: Sẽ có 2^n dãy nhị phân cho một n
Ví dụ: với n =3 thì ta phải sinh được 1 dãy nhị phân: 000, 001, 010, 011,
tử đó sẽ được tăng giá trị lên 1 và các phần tử bên phải phần tử đó sẽ
bị giảm hết về 0, các phần tử bên trái phần tử đó giữ nguyên trạng thái
Mã giả code:
1 Nhập
2 Khởi tạo mảng a[] gồm n phần tử, tất cả đều là 0
3 Bool final = false
4 While final = false{
Code: Sinh nhi phan
Trang 16Đoạn code hoàn chỉnh
Kết quả chạy code với nhập n =3
Trang 17b) Bài toán sinh tổ hợp chập k của N phần tử
• Bắt đầu từ vị trí đầu tiên và đi đến vị trí cuối cùng
• Cấu hình đầu tiên sẽ là từ 1 đến phần từ k
từ i đến N-k+i Như ví dụ trên với i = 1 thì giá trị a[i] sẽ chạy từ 1 đến 4-2+1 = 3, với i=2 giá trị a[i] sẽ chạy từ 2 đến 4-2+2 = 4
Mã giả code:
1 Nhập N và k
2 Khởi tạo mảng a[] gồm N phần tử, phần từ a[i] có giá trị = i
3 Bool final = false
4 While final = false{
Code: Sinh to hop chap K cua N phan tu
Trang 18Đoạn code hoàn chỉnh
Kết quả chạy code với nhập n =3
Trang 19V Ví Dụ Phương Pháp quay lui:
a) Sinh xâu nhị phân có độ dài N
Giải thích code: nhập N số nhị phân cần tim Khởi tạo mảng a[100] để lưu kết
quả Truyền vào hàm thuatToan(1) là để bắt đầu từ số nhị phân đầu tiền Vòng for gán các giá trị có thể của số nhị phân là 0 và 1 sau đó kiểm tra nếu i = n thì
in ra kết quả nếu không gọi hàm thuatToan(2) và gán a[2] bằng các giá trị 0 hoặc 1 và kiểm tra 2 # 3 tiếp tục gọi thuaToan(3) gán a[3] bằng 0 kiểm tra i = 3 trả ra kết quả 000 Vẫn trong vòng for đó gán a[3] = 1 kiểm tra i = 3 trả ra kết quả 001 Quay lại vòng for của thuatToan(2) gán a[2] = 1 kiểm tra i = 2 chưa bằng 3 lại gọi thuatToan(3) và trả ra kết quả 010 và 011 Backtrack lại
thuatToan(1) gán a[1] = 1 và tiếp tục như vậy
Trang 20b) Tổ hợp chập K của N
Giải thích code: cơ bản thuật toán này hoạt động giống thuật toán trên Thay
đổi là vòng for kết thúc khi sinh đến k phần tử và tìm giá trị nhỏ nhất của a[i] là giá giá trị đứng trước nó cộng thêm 1: j = a[i-1] +1; và giá trị cao max của a[i]:
j = N – K + i; ví dụ ở vị trí i = 1 thì giá trị max của i = 6 – 4 + 1 = 3 để đảm bảo rằng nếu j tăng lên 4 thì số được sinh ra chỉ có 456 không phải là 4 phần tử
Trang 21
Giải thích code: bài toán này sẽ có thêm điều kiện là 1 phần tử không được
xuất hiện 2 lần trong một cấu hình vì vậy sẽ dùng mảng visited[100] = {0} để đánh dấu chưa xét và đã xét Thuật toán hoạt động như sau:
Trang 22d) Bài toán N-queen (bài toán kinh điển của thuật toán quay lui)
Để giải quyết bài toán thì ta phải xác định các các nước mà 2 quân Hậu có thể
ăn nhau là theo chiều dọc, ngang và chéo Vì vậy chúng ta sẽ xếp mỗi quân Hậu
ở 1 hàng và đánh dấu cột và 2 đường chéo quân Hậu đó có thể đi thì đến hàng tiếp theo xét các ô mà quân hậu 1 đó không thể ăn để đặt quân hậu 2 và cứ như thế đánh dấu các cột và 2 đường chéo cho đến khi xếp được quân Hậu thứ N