Bài tốn: Cho tập hợp X = {1, 2, ..., n}, hãy liệt kê tất cả hốn vị của X. Mỗi hốn vị n phần tử của tập X cĩ thể biểu diễn bởi bộ cĩ thứ tự gồm n thành phần a = {a1, a2,.., an} thoả ai ∈ X; i = 1, 2, .., n; ap ≠ aq nếu p ≠ q. Trên các tập hốn vị của X ta định nghĩa thứ tự của các hốn vị như sau:
a = (a1, a2,..., an) được gọi là cĩ thứ tự trước hốn vị a’=(a’1,a’2,..,a’n). Cĩ ký hiệu a < a’ nếu tìm được chỉ số k sao cho.
a1 = a’1, a2 = a’2,..., ak-1 = a’k-1, ak <a’k.
Ví dụ X = {1, 2, 3, 4} khi đĩ thứ tự hốn vị n = 4 được liệt kê như sau: {{1, 2, 3, 4}, {1, 2, 4, 3}, {1, 3, 2, 4} {1, 3, 4, 2} {1, 4, 2, 3} {1, 4, 3, 2} {2, 1, 3, 4}, {2, 1, 4, 3}, {2, 3, 1, 4} {2, 3, 4, 1} {2, 4, 1, 3} {2, 4, 3, 1} {3, 1, 2, 4}, {3, 1, 4, 2}, {3, 2, 1, 4} {3, 2, 4, 1} {3, 4, 1, 2} {3, 4, 2, 1} {4, 1, 2, 3}, {4, 1, 3, 2}, {4, 2, 1, 3} {4, 2, 3, 1} {4, 3, 1, 2} {4, 3, 2, 1}} Hốn vị đầu tiên là: {1, 2, ..., n-1, n} và hốn vị cuối cùng là {n, n-1,..,2, 1}.
Khi đĩ hốn vị kế tiếp sinh ra phải lớn hơn hốn vị hiện tại, và hơn nữa nĩ phải đủ lớn hơn hốn vị hiện tại theo nghĩa khơng cĩ hốn vị nào khác chen vào giữa nĩ khi sắp theo thứ tự từ điển.
Giả sử cĩ hốn vị sau: <3, 2, 6, 5, 4, 1>, ta xét 4 phần tử cuối cùng, do chúng được sắp theo thứ tự giảm dần. Khi đĩ ta hốn vị 4 giá trị này thì cũng chỉ được hốn vị nhỏ hơn hốn vị hiện tại. Như vậy ta phải xét đến a2 = 2, ta phải thay giá trị này, nhưng thay giá trị nào? ta khơng thể thay bằng 1 vì nếu như vậy sẽ được hốn vị nhỏ hơn, khơng thể thay bằng 3 vì giá trị này đã cĩ rồi a1 = 3 (phần tử sau khơng được chọn vào những giá trị xuất hiện ở phần tử trước). Chỉ cịn lại giá trị 4, 5, 6. Vì cần một hốn vị đủ lớn nên ta chọn a2 = 4. Cịn các giá trị a3, a4, a5, a6 sẽ lấy trong tập {2, 6, 5, 1}. Cũng do tính chất vừa đủ lớn nên ta sẽ tìm biểu diễn nhỏ nhất của 4 số này để gán cho a3, a4, a5, a6, là <1, 2, 5, 6> vậy ta được hốn vị mới là <3, 4, 1, 2, 5, 6>
Nhận xét: đoạn cuối của hốn vị hiện tại được sắp giảm dần. số a5 là 4 là số nhỏ nhất trong đoạn cuối lớn hơn a2 = 2. Nếu đổi chỗ a5 cho a2 thì ta được a2 = 4 và đoạn cuối vẫn được xếp giảm dần là <6, 5, 2, 1> khi đĩ muốn biểu diễn nhỏ nhất cho các giá trị trong đoạn cuối thì ta chỉ cần đảo ngược đoạn cuối.
Ví dụ trong hốn vị hiện tại <2, 1, 3, 4> cĩ hốn vị kế tiếp là <2, 1, 4, 3>. Ta cĩ thể xem <2, 1, 3, 4> cĩ đoạn cuối giảm dần là một phần tử <4>.
Vậy kỹ thuật sinh hốn vị kế tiếp từ hốn vị hiện tại cĩ thể xây dựng như sau:
Xác định đoạn cuối giảm dần dài nhất, tìm phần tử ai đứng trước đoạn cuối đĩ. Điều này đồng nghĩa với việc tìm từ vị trí sát cuối dãy lên đầu, gặp chỉ số i đầu tiên thoả mãn ai < ai+1.
Nếu tìm thấy chỉ số i như trên: trong đoạn cuối giảm dần, tìm phần tử ak nhỏ nhất thoả mãn ak > ai. Do đoạn cuối giảm dần nên thực hiện bằng cách từ cuối dãy lên đầu gặp chỉ số k đầu tiên thoả ak > ai.
o Đảo giá trị ak và ai.
o Lật ngược thứ tự đoạn cuối giảm dần (ai+1 đến ak) trở thành tăng dần Nếu khơng tìm thấy tức là dãy giảm dần, đây là cấu hình cuối cùng.
Chương trình minh họa 3: Liệt kê hốn vị n phần tử.
int n, P[MAX], Stop;
void Next_Permutation() { int j, k; j = n -1; while (j>0 && P[j]> P[j+1]) j--; if (j == 0) Stop = 1; else { k = n; while (P[j] > P[k]) k--; Swap(P[j], P[k]); l = j+1; r = n; while (l < r) { Swap(P[l], P[r]); l++; r--; } }// end else } void Permutation() {
Stop = 0; while (! Stop) { Result(); Next_Permutation(); } } void Result() {
static int count=0;
printf(“\n Hoan vi thu %d”, ++count); for(int i=1; i <= n; i++)
printf(”%3d”, P[i]); }
int main() {
printf(“Nhap n: ”); scanf(“%d”, &n);
for(int i=1; i <= n; i++) P[i] = i; return 0; } Khả năng chọn x2 với x1 đã chọn 44