Giáo trình Toán rời rạc
Trang 1Chương IV
ĐẾM CÁC PHẦN TỬ
I) Các phương pháp đếm cơ bản
1 Mở đầu
Số serial của phiên bản Windows 98 của hãng MicroSoft gồm một dãy 25 chữ số thập phân và chữ cái trong bộ mẫu tự tiếng Anh (không phân biệt hoa/ thường) Vậy có bao nhiêu số serial khã dĩ dùng cho các bản copy của phiên bản đó? Có tất cả bao nhiêu lời gọi đệ qui xảy ra khi thực hiện đoạn chương trình giải bài toán tháp Hà Nội với kích thước dữ liệu đầu vào N? Tìm kiếm câu trả lời cho các câu hỏi tương tự như vậy đều dẫn đến việc thực hiện các phép đếm Khi số lượng thực hiện thao tác đếm với số lượng cần phải đếm là rất lớn hoặc các cấu hình cần phải đếm thay đổi phức tạp thì việc phát triễn các kỹ thuật đếm là rất cần thiết
2 Các nguyên lý đếm cơ bản
Các kỹ thuật đếm thường dựa trên nguyên tắc chia bài toán đếm thành các bài toán nhỏ hơn có cấu hình quen thuộc mà việc đếm các phần tử trong cấu hình đó chỉ cần dựa trên một số các nguyên lí đếm cơ bản Dĩ nhiên bài toán đếm chỉ có nghĩa khi nó dẫn đến đáp số hữu hạn (mặc dù đáp số này có thể là một con số khổng lồ!) Vì vậy mọi tập hợp X được xét sau đây đều mặc định là tập hữu hạn và N(X) để chỉ số phần tử của X
o Nguyên lí cộng:
Nếu A và B là hai tập hợp rời nhau thì N(A∪B)=N(A)+N(B)
Ví dụ:
Có bao nhiêu cách chọn một vận động viên từ danh sách 12 người của Huyện Chợ Lách, danh sách 14 người của Huyện Châu Thành và danh sách 20 người của Huyện Ba Tri biết rằng không có vận động viên nào thi đấu cho hơn một huyện?
Lời giải:
Rõ ràng các danh sách là các tập rời nhau và việc lựa chọn vận động viên trên mỗi danh sách là độc lập với nhau Vì vậy số cách lựa chọn vận động viên là: 12+14+20=46 cách
Ví dụ:
Xét đoạn mã chương trình viết bằng ngôn ngữ Pascal sau:
Trang 2Var K,n1,n2,n3:byte;
Begin
{ }
K := 7; n1:=5; n2:=10; n3:=15;
For i:=1 to n1 do K:=K+1; For j:=1 to n2 do K:=K+1;
For t:=1 to n3 do K:=K+1; writeln(‘K = ’,K);
{ }
Đoạn chương trình này khi thực thi xong thì kết xuất của dòng lệnh cuối cùng là gì?
Lời giải:
Ở mỗi lần lặp trong mỗi vòng lặp K đều tăng lên một đơn vị Sau mỗi vòng lặp K tăng lên đúng bằng số lần lặp K được khởi tạo trước vòng lặp đầu tiên bằng
7 Vì các vòng lặp là rời nhau nên sau vòng lặp thứ nhất K=7+n1, sau vòng lặp thứ hai K=7+n1+n2, sau vòng lặp thứ ba K=7+n1+n2+n3 Kết xuất của dòng lệnh cuối cùng là:
K = 37
o Nguyên lí nhân:
Nếu mỗi thành phần ai của bộ có thứ tự (a1,a2, ,ak) có ni khả
năng lựa chọn (1 i k≤ ≤ ) thì số cấu hình tạo ra là tích số các
khả năng này Nói cách khác: Nếu ∀i(1≤ ≤i k a A), i∈ i thì:
N(A1xA2x x Ak) = N(A1).N(A2) N(Ak)
Ví dụ:
Có bao nhiêu cách chọn ba vận động viên thuộc ba huyện theo thứ tự từ danh sách 12 người của Huyện Chợ Lách, danh sách 14 người của Huyện Châu Thành và danh sách 20 người của Huyện Ba Tri biết rằng mỗi huyện được chọn đúng một vận động viên?
Lời giải:
Rõ ràng mỗi cách lựa chọn là một bộ ba (a1,a2,a3) Trong đó a1 là vận động viên của Chợ Lách, a2 là vận động viên của Châu Thành, a3 là vận động viên của Batri Có 12 cách lựa chọn a1 Với mỗi cách lựa chọn a1 đó có 14 cách lựa chọn a2 Với mỗi cách lựa chọn a1 và a2 đó có 20 cách lựa chọn a3 Vì vậy số cấu hình lựa chọn có thể có là: 12x14x20 =3360
Ví dụ:
Xét đoạn mã chương trình viết bằng ngôn ngữ Pascal sau:
Var K,n1,n2,n3:word;
Begin
{ }
Trang 3K := 7; n1:=5; n2:=10; n3:=15;
For i:=1 to n1 do
For j:=1 to n2 do
For t:=1 to n3 do
K:=K+1;
writeln(‘K = ’,K);
{ }
Cho bieât keât xuaât cụa doøng leônh cuoâi cuøng laø gì?
Lôøi giại:
ÔÛ ñađy ta coù ba voøng laịp FOR loăng nhau Trình bieđn dòch Pascalseõ bieđn dòch
ñeơ vôùi moêi laăn laịp cụa voøng laịp beđn ngoaøi thì voøng laịp ngay beđn trong noù ñöôïc thöïc hieôn ñaăy ñụ caùc laăn laịp Vaôy moêi laăn laịp cụa ba voøng laịp loăng nhau ñoù töông öùng vôùi moôt caâu hình laø boô ba coù thöù töï (i,j,t) ÔÛ moêi laăn laịp K ñeău taíng leđn moôt ñôn vò Sau taât cạ laăn laịp K taíng leđn ñuùng baỉng soâ laăn laịp K ñöôïc khôûi táo tröôùc khi vaøo voøng laịp baỉng 7 Vì i veùt qua taât cạ n1 giaù trò khaùc nhau, j veùt qua taât cạ n2 giaù trò khaùc nhau, t veùt qua taât cạ n3 giaù trò khaùc nhau neđn soâ caâu hình ñöôïc táo ra cho boô (i,j,t) laø n1.n2.n3 Ñoù cuõng chính laø toaøn boô soâ laăn laịp Vaôy sau khi toaøn boô voøng laịp ñaõ thöïc hieôn xong thì K=7 + n1.n2.n3 Keât xuaât cụa doøng leônh cuoâi cuøng laø:
K = 757
Ví dú:
Soâ serial cụa phieđn bạn Windows 98 cụa haõng MicroSoft goăm moôt daõy 25 chöõ soâ thaôp phađn vaø chöõ caùi trong boô maêu töï tieâng Anh (khođng phađn bieôt hoa/ thöôøng) Vaôy coù bao nhieđu soâ serial khaõ dó duøng cho caùc bạn copy cụa phieđn bạn ñoù?
Lôøi giại:
Moêi soâ serial laø moôt daõy 25 chöõ caùi vaø chöõ soâ Chaúng hán:
HQ6K2-QPC42-3HWDM-BF4KJ-W4XWJ
Bạng maêu töï tieâng anh coù 26 chöõ caùi (Töø A ñeân Z) Soâ chöõ soâ thaôp phađn laø
10 chöõ soâ (töø kí töï 0 ñeân kí töï 9) Vaôy moêi vò trí trong soâ serial coù 36 löïa chón khaùc
löïa chón soâ serial khaùc nhau Ñađy quạ thaôt laø moôt con soâ kinh khụng!
o Nguyeđn lí buø tröø:
Khi thöïc hieôn nguyeđn lyù coông vaøo baøi toaùn ñeâm ta phại giạ thieât hai taôp caăn ñöôïc ñeâm A vaø B laø rôøi nhau Neâu khođng coù giạ thieât ñoù thì buoôc phại loái bôùt caùc phaăn töû truøng nhau cụa hai taôp bò ñeẫm qua quaù moôt laăn:
Trang 4Ví dụ: Có bao nhiêu xâu nhị phân độ dài 8 bit với 3 bit 111 đứng đầu hoặc 2 bit 00 đứng
cuối
Lời giải:
Số xâu độ dài 8 bit với 3 bit 111 đứng đầu là: 25 = 32 (Áp dụng nguyên lí nhân đối với 5 bít cuối do chỉ còn 5 bit cuối là thay đổi)
Tương tự, số xâu độ dài 8 bit với 2 bit 00 đứng cuối là: 26 = 64 (do chỉ còn 6 bit đầu là thay đổi)
Số xâu vừa có 3 bit đứng đầu 111, vừa có 2 bit đứng cuối 00 là: 23= 8
Vậy số xâu thỏa điều kiện đòi hỏi là: (32+64) - 8 = 88
Tổng quát hơn:
Giả sử A1,A2, ,Am là các tập Khi đó:
1
Trong đó Nk là tổng các phần tử của tất cả các giao của k tập lấy từ m tập đã cho.
Cụ thể N1 là tổng các phần tử của tất cả các giao của 1 tập lấy từ m tập đã cho Hay:
N1=N(A1)+N(A2)+ +N(Am)
N2 là tổng các phần tử của tất cả các giao của 2 tập lấy từ m tập đã cho Có tất cả 2
m
C
phần giao như vậy Hay:
N2 = N(A1∩A2)+N(A1∩A3)+ +N(Ai∩Aj)+
m
i j
i j
=
≠
∩
∑
1 2
j
, , , 1
các i khác nhau đôi một
k k
m
i i i
=
Nm là tổng các phần tử của tất cả các giao của m tập lấy từ m tập đã cho Hay Nm=N(A1
∩A2∩A3∩ ∩Am)
Ta chấp nhận không chứng minh tính chất trên
Ví dụ:
Trong tập X={1,2, , 10000} có bao nhiêu số không chia hết cho bất cứ số nào trong
ba số 3,4,7?
Lời giải:
Đặt Ai là tập các số thuộc X mà chia hết cho i {i=3,4,7}
Trang 5Khi đó A3∪A4∪A7 là tập các số thuộc X mà chia hết cho ít nhất một trong ba số 3,4,7 Vậy theo công thức trên ta sẽ có số các số chia hết cho ít nhất một trong 3 số 3,4,7 là:
N(A3∪A4∪A7) = N1-N2 + N3.
Sử dụng toán tử div cho phép chia nguyên, ta cóù:
N1 = N(A3)+N(A4)+N(A7) = (10000 div 3)+(10000 div 4)+(10000 div 5)
= 3333+2500+1428 = 7261
N2 = N(A3∩A4)+N(A3∩A7)+N(A4∩A7)
= (10000 div (3x4))+(10000 div (3x7))+(10000 div (4x7)) = 833+476+357 =1666
N3 = N(A3∩A4∩A7)=(10000 div (3x4x7))=119.
Suy ra số các số thuộc X mà không chia hết cho số nào trong 3 số 3,4,7 là:
N(X)-N(A3∪A4∪A7)=10000 - (7261-1666+119) = 4286.
o Biểu đồ cây.
Trong một số trường hợp giải bài toán đếm bằng biểu đồ cây làm cho lời giải được trực quan và sáng sủa hơn
Ví dụ:
Xem đoạn chương trình giải bài toán tháp Hà Nội1 sau đây:
Procedure Ch(n:số đĩa; A,B,C : Vị trí)
{Thủ tục Chuyển n đĩa từ vị trí A sang vị trí C lấy vị trí B làm trung gian}
BEGIN
IF (n>=1) THEN
Ch(n-1,A,C,B)
Ch(n-1,B,A,C)
END
Chúng ta biết rằng các kết xuất của đoạn chương trình trên chỉ có thể thực hiện
khi toàn bộ các lời gọi đệ qui đã được thực thi Với n là dữ liệu nhập câu hỏi đặt ra là
có tất cả bao nhiêu lời gọi đệ qui đã được thực hiện khi chương trình thực hiện Output?
Biểu đồ cây sau đây (với n=3) cho thấy các lời gọi đệ qui đã diễn ra như thế nào
1 Người viết giả thiết rằng người đọc đã biết phát biểu của bài toán này Và ở thời điểm đọc phần này người đọc đã hiểu về lời gọi đệ qui trong một ngôn ngữ lập trình nào đó.
Trang 6Biểu đồ trên cho thấy mỗi lời gọi Ch(n,x,x,x) lại gọi đệ qui tiếp hai lời gọi Ch(n-1,x,x,x) nữa trừ khi n=0 Như vậy số lời gọi đệ qui là:
K = 1+1.2+2.2+(2.2).2+(2.2.2).2+ +(2n-1).2
Hay K=1+21+22+23+24+ +2n
Có thể dùng truy chứng để chứng minh rằng K=2n+1 - 1 ∀ ≥n 0,n N∈
Do đó với n=3 có tất cả 24-1 = 15 lời gọi thủ tục Ch Mỗi lời gọi thủ tục Ch với n>0 ứng với một lần kết xuất Điều đó chứng tỏ rằng với 3 đĩa cần phải có 23-1=7 lần chuyển đĩa mới đưa được hết số đĩa từ vị trí A sang vị trí C
Rõ ràng nếu không dùng biểu đồ cây trong trường hợp này ta sẽ gặp khó khăn rất nhiều trong việc hình dung ra phép đếm số lần dời đĩa cần thiết
o Nguyên lý Dirichlet:
Nguyên lí Dirichlet còn gọi là nguyên lí chuồng thỏ và phát biểu như sau:
Trang 7“Nếu số thỏ nhiều hơn số chuồng thì có ít nhất một chuồng có nhiều hơn một con thỏ”
Ví dụ: Nếu chấm điểm bài thi theo thang điểm 10 và điểm chấm là số nguyên
không âm thì trong một phòng thi 15 người thế nào cũng có hai người có cùng một điểm số
Ví dụ: Trong một lớp 20 sinh viên thì thế nào cũng có ít nhất 2 sinh viên sinh
cùng tháng
Nguyên lí chuồng thỏ phát biểu rất đơn giản - gần như hiển nhiên -Tuy nhiên lại có những áp dụng rất tinh tế trong các chứng minh, đặc biệt là các chứng minh liên quan đến sự tồn tại.
Ví dụ:
Chứng tỏ rằng trong n+1 số nguyên dương không lớn hơn 2n tồn tại ít nhất một số nguyên chia hết cho một số nguyên khác trong đó
Lời giải:
Ta nhận xét rằng mỗi số nguyên dương đều có thể viết được dưới dạng 2 k j
j q
trong đó kj là một số nguyên không âm và qj là một số nguyên lẻ Vậy ta viết n+1 số nguyên dương nói trên thành dãy:
1
1
2 k q 2
2
2 k q 3
3
2 k q 2 k n
n
1
2 k n
n q
+
Vì lẽ chỉ có n số nguyên dương lẻ bé hơn 2n (số chuồng: n)
nên trong dãy: q1 q2 qn qn+1 (số thỏ: n+1)
phải tồn tại hai số qi và qj bằng nhau (áp dụng nguyên lí Dirichlet) Và do đó một trong hai số 2 k i
i
q , 2 k j
j
q phải chia hết cho số còn lại.
II) Giải tích tổ hợp
Do phần lớn nội dung này sinh viên đã được học khá kỹ lưỡng từ trường phổ thông nên giáo trình này chỉ nhắc lại các định nghĩa và kết quả chủ yếu mà thôi Các chứng minh và các ví dụ sẽ bị bỏ qua
1 Chỉnh hợp, hoán vị và tổ hợp:
Chỉnh hợp lặp chập m của n phần tử :
Định nghĩa:
Một chỉnh hợp lặp chập m của n phần tử của tập { , , , }b b1 2 b là một dãy có thứ n
tự (a1,a2, , am) trong đó ∀ =i 1, ,m a i∈{ , , , }b b1 2 b n
Định lí:
Số chỉnh hợp lặp chập m của n phần tử là A n m = nm
Trang 8Chỉnh hợp không lặp chập m của n phần tử :
Định nghĩa:
Một chỉnh hợp không lặp chập m của n phần tử (m≤n) của tập { , , , }b b1 2 b là n
một dãy có thứ tự (a1,a2, , am) trong đó ∀ =i 1, ,m a i ∈{ , , , }b b1 2 b n và ai ≠aj nếu i≠
j
Định lí:
Số chỉnh hợp không lặp chập m của n phần tử là m
n
P = n.(n-1).(n-2) (n-m+1).
Hoán vị của n phần tử:
Định nghĩa:
Một hoán vị của n phần tử là một chỉnh hợp không lặp chập n của n phần tử đó Định lí:
Số hoán vị của n phần tử là Pn = n!
Tổ hợp m phần tử của n phần tử
Định nghĩa:
Một tổ hợp gồm m phần tử của một tập n phần tử là một tập con gồm m phần tử
của tập đó
Định lí:
m
m
C
−
Định lí:
1
2 Hệ số nhị thức:
Xét biểu thức (a+b)n
Biểu thức này là tích gồm n thừa số:
Khai triển tích này ta được một tổng các số hạng dạng aibj trong đó a được lấy i lần trong n thừa số khác nhau còn b được lấy j lần trong các thừa số còn lại (ie: j = n-i)
Vậy khai triển của biểu thức gồm một tổng các đơn thức dạng K(i).ai.b(n-i) :
0
( )
n
i n i i
=
đồng dạng aib(n-i) làm thừa số chung
Trang 9Mỗi số hạng đồng dạng aib(n-i) như vậy tương ứng với một tổ hợp gồm i vị trí của
a trong n vị trí có thể có của (1) Suy ra K(i) chính là số tổ hợp chập i của n phần tử
Hay:
0
n
i i n i n i
=
∑
Ví dụ:
(a+b)5=C a b +50 0 5 1 1 4
5
5
5
5
5
C a b
Sau khi tính các hệ số, ta được:
(a+b)5=1.a0b5+5.a1b4+10.a2b3+10.a3b2+5.a4b1+1.a5b0
Hay: (a+b)5= b5+5.ab4+10.a2b3+10.a3b2+5.a4b+a5
Trong thực tế tính toán ta không lần lượt tính các hệ số nhị thức C như vậy mà n i hay dựa vào công thức (*) và (**) để xây dựng lên bảng các hệ số sau đây (gọi là tam giác Pascal)
1 Các hệ số ứng với n = 0
1 1 Các hệ số ứng với n = 1
1 2 1 Các hệ số ứng với n = 2
1 3 3 1 Các hệ số ứng với n = 3
Bảng này cho chúng ta tham chiếu các hệ số nhị thức thuận tiện hơn nhiều Ví vụ để viết triển khai của (a+b)6 ta chỉ cần xây dựng bảng trên thêm một hàng nữa để có ngay:
(a+b)6=a6+6a5b+15a4b2+20a3b3+15a2b4+6ab5+b6
3 Thuật toán sinh các hoán vị.
Ta thấy số tổ hợp n m !( ! )!
n C
m n m
=
− là một hàm theo n và tăng rất nhanh theo n (với
m khác 0) Các phép đếm liên quan đến các cấu hình phát sinh có thể gặp trường hợp số cấu hình phát sinh quá nhiều (gọi là bùng nổ tổ hợp) mà ngay đến máy tính cũng không tính xuể! Mặt khác khi giải một số bài toán liên quan đến việc liệt kê các cấu hình cần có phương pháp để việc duyệt các cấu hình không bị bỏ sót bất cứ cấu hình có thể có nào Để làm được việc đó bài toán liệt kê các cấu hình phải thỏa mãn hai điều kiện:
a) Có thể xây dựng được một thứ tự trên tập các cấu hình cần liệt kê
Trang 10b) Xây dựng được giải thuật để dưa ra được một cấu hình mới, kế tiếp cấu hình đang xét nếu cấu hình đang xét chưa phải là cấu hình cuối cùng trong dãy cần liệt kê Điều kiện a) tùy thuộc rất nhiều vào bản chất của bài toán đang xét Giả sử giải quyết được điều kiện a) thì giải thật nói trong điều kiện b) gọi là thuật toán sinh kế tiếp Có thể mô tả thuật toán đó như sau:
Procedure Generate
BEGIN Xây dựng cấu hình ban đầu Stop:=FALSE
While not stop do
Begin
Thông báo cấu hình đang có
IF cấu hình đang có là cấu hình cuối cùng THEN
Stop:=TRUE ELSE
Sinh cấu hình kế tiếp End
END
Ví dụ:
Xây dựng một chương trình liệt kê các hoán vị của một tập n phần tử.
Lời giải:
Việc liệt kê các hoán vị của một tập có n phần tử có thể được mô hình hóa bằng việc liệt kê các hoán vị của tập X={1, 2, 3, , n}
Mỗi hoán vị của X là một bộ có thứ tự (a1,a2, ,an) thỏa mãn:
1, , : i
a ≠a nếu i≠j
Trên tập các hoán vị của X có thể xác định một thứ tự từ điển p (đọc là: nhỏ hơn) như sau:
(a1,a2, ,an) p (a’1,a’2, ,a’n) nếu và chỉ nếu
Với thứ tự đó thì cấu hình ban đầu là (1,2,3, ,n) và cấu hình kết thúc trong liệt kê là (n,n-1,n-2, ,1)
Vấn đề còn lại là xây dựng một phương pháp sinh ra cấu hình kế tiếp từ một hoán vị đang biết (mà không phá vở thứ tự đã được định nghĩa)
Để trực quan ta thử xét với X={1,2,3,4,5,6} Giả sử cấu hình đang có là
(a1,a2,a3,a4,a5,a6)=(3,6,2,5,4,1)
Trang 11Để sinh cấu hình kế tiếp đảm bảo thứ tự nói trên ta cần:
Duyệt từ phải qua trái trên hoán vị đang có chỉ số j đầu tiên thỏa mãn aj<aj+1:
Ta được j=3 (vì a3=2 <a4=5) Làm điều này vì tại vị trí này ta có khả năng đổi vị trí của a3 (hiện tại) với một phần tử khác ở phía bên phải của a3 để có một cấu hình lớn hơn no.ù
Tìm trong các số bên phải của aj số ak nhỏ nhất còn lớn hơn aj:
Ta được k=5 (vì a5=4 là số nhỏ nhất trong các số bên phải a3 mà lớn hơn a3 Làm điều này để đảm bảo cho cấu hình kế tiếp được sinh ra là nhỏ nhất trong số các cấu hình lớn hơn cấu hình hiện tại
Đổi chỗ aj với ak để được một cấu hình khác: Ta được (3,6,4,5,2,1).
Lật ngược đoạn từ aj+1 đến an: Do quá trình xây dựng cấu hình nên dãy con bên
phải aj hiện tại là dãy lớn nhất của cấu hình con này Lật ngược dãy con bên phải này để được cấu hình sinh ra là cấu hình nhỏ nhất trong các cấu hình lớn hơn cấu hình ban đầu Ta được hoán vị (3,6,4,1,2,5)
Giải thuật:
Procedure SinhHoanvi
BEGIN
Hoán vị đang có:=(1,2,3, ,n)
WHILE Hoán vị đang có ≠(n,n-1,n-2, ,1) DO
Begin
Output(Hoán vị đang có)
Duyệt từ phải qua trái trên hoán vị đang có chỉ số j đầu tiên thỏa mãn aj<aj+1 Tìm trong các số bên phải của aj số ak nhỏ nhất còn lớn hơn aj
Đổi chỗ aj với ak
Lật ngược đoạn từ aj+1 đến an để được hoán vị (đang có) mới
End
END
Ví dụ: Với X={1,2,3} áp dụng giải thuật trên ta được các hoán vị theo thứ tự từ điển là:
Không phải bài toán liệt kê cấu hình nào cũng cho phép xây dựng một thứ tự tuyến tính để từ đó xây dựng được một giải thuật sinh phần tử Có một thuật toán gọi là “quay lui” sẽ được nghiên cứu trong các chương sau giúp cho việc tìm ra một cấu hình thích hợp
một cách tổng quát hơn