Phát biểu: Cho tập hợp X = {1, 2,.., n}. Hãy liệt kê tất cả tập con k phần tử của X. Mỗi tập con k phần tử của X cho thể biểu diễn như bộ thứ tự:
a = (a1, a2,.., ak) thỏa mãn 1 ≤ a1≤ a2≤ ... ≤ ak≤ n. Trên tập con k phần tử của X, ta định nghĩa thứ tự của các tập con như sau:
Ta nĩi tập a = (a1, a2,.., ak) cĩ thứ tự trước tập a’ = (a’1, a’2,.., a’k) theo thứ tự từ điển và ký hiệu là a < a’ nếu tìm được j sao cho: a1 = a’1, a2 = a’2..., aj-1 = a’j-1 và aj < a’j.
Ví dụ với n = 5, k = 3, ta liệt kê 10 tập con của nĩ như sau:
{{1,2,3},{1,2,4}{1,2,5}{1,3,4}{1,3,5}{1,4,5}{2,3,4}{2,3,5}{2,4,5}{3,4,5}} + Ta thấy cấu hình đầu tiên là {1, 2..., k}
+ Cấu hình kết thúc là {n-k+1, n-k+2,.., n}.
Nhận xét: chúng ta sẽ in ra tập con với các phần tử của nĩ theo thứ tự tăng dần. Biểu diễn tập con là một dãy a{a1, a2,..., ak} trong đĩ a1< a2 <...<ak. Ta nhận thấy giới hạn trên của ak là n, của ak-1 là n-1, của ak-2 là n-2.
Tổng quát giới hạn trên của ai = n-k+i.
Cịn giới hạn dưới của của ai (giá trị nhỏ nhất ai cĩ thể nhận) là ai-1 + 1.
Như vậy nếu ta đang cĩ một dãy x đại diện cho tập con, nếu x là cấu hình kết thúc thì cĩ nghĩa tất cả các phần tử trong x đều đạt tới giới hạn trên thì quá trình sinh kết thúc. Nếu khơng thì phải phát sinh một dãy x tăng dần thỏa mãn đủ lớn hơn dãy x và khơng cĩ dãy nào chen vào giữa hai dãy theo thứ tự từ điển.
Ví dụ: n = 9, k = 6, cấu hình đang cĩ <1, 2, 6, 7, 8, 9>, các phần tử a3⇒ a6 đã đạt đến giới hạn nên ta khơng thể tăng các phần tử này được, ta phải tăng a2 từ 2 lên thành 3. Được cấu hình mới là <1, 3, 6, 7, 8, 9> cấu hình này thoả mãn lớn hơn cấu hình cũ, nhưng chưa thoả mãn tính chất vừa đủ lớn do đĩ ta phải thay a3, a4, a5, a6 bằng giới hạn dưới của nĩ như sau:
a3 = a(3-1=2) + 1 = 3 + 1 = 4 a4 = a(4-1=3) + 1 = 4 + 1 = 5 a5 = a(5-1=4) + 1 = 5 + 1 = 6 a6 = a(6-1=5) + 1 = 6 + 1 = 7
Vậy cấu hình tiếp theo <1, 3, 4, 5, 6, 7> là cấu hình cần tìm. Do đĩ muốn xác định cấu hình tiếp ta thấy a6 = 7 chưa đạt đến giới hạn ta chỉ cần tăng a6 lên một là được cấu hình tiếp theo: <1, 3, 4, 5, 6, 8>.
Vậy kỹ thuật sinh tập con kế tiếp từ tập x đã cĩ cĩ thể xây dựng như sau: Tìm từ cuối lên đầu dãy cho tới khi gặp phần tử ai chưa đạt đến giới hạn n-k+i.
Nếu tìm thấy:
o Tăng ai đĩ lên 1.
o Đặt tất cả phần tử phía sau ai bằng giới hạn dưới.
Nếu khơng tìm thấy tức là phần tử đã đạt giới hạn trên, đây là cấu hình cuối cùng. Kết thúc thuật tốn.
Chương trình minh họa 2: liệt kê tập con k phần tử của n.
void Next_SubSet() {
int i,j;
i = k; // duyệt từ cuối dãy
// lặp khi chưa tìm được phần tử chưa tới giới hạn while (i >0 && A[i] == n-k+i)
i--; // duyệt về đầu
if ( i > 0) {
A[i] = A[i] +1; // tăng một đơn vị
// cho các phần tử cịn lại qua giới hạn dưới for(j = i+1; j <= k; j++)
A[j] = A[j-1]+ 1 }
else
Stop = 1; // kết thúc phát sinh cấu hình
} void GenerateSet() { Stop = 0; while (!Stop) {
Result(); // xuất cấu hình hiện tại
Next_SubSet(); // qua cấu hình khác
} }
void Result() {
static int count=0;
printf(“Tap con thu %d”, ++count); for(i=1; i <=k; i++)
printf(“%3d”, A[i]); }
int main() {
printf(“Nhap n: ”); scanf(“%d”, &n); printf(“Nhap k: ”); scanf(“%d”, &k); for(int i=1; i <= n;i++)
A[i] = i; GenerateSet(); getch();
return 0; }