Thuật toán kết hợp: In tất cả các kết hợp có thể có của R
Sự kết hợp là gì?
Sự kết hợp là một kiểu sắp xếp của một số đối tượng nhất định. Theo thuật ngữ toán học, Kết hợp là một tập hợp các lựa chọn/lựa chọn các mục từ một tập hợp các mục/đối tượng duy nhất. Ở đây, thứ tự của các mục không quan trọng. Nó còn được gọi là phương pháp tính tổng kết quả của một sự kiện, trong đó thứ tự của kết quả không quan trọng.
Ví dụ: bạn được tặng một chiếc túi có 5 màu khác nhau và được yêu cầu tạo một mẫu có 3 màu bất kỳ. Bạn cũng có thể chọn 3 màu bất kỳ trong số 4 màu, sau đó sắp xếp chúng theo thứ tự khác nhau.
Giả sử các màu là RGYBI(R= Red, G= Green, Y= Yellow, B= Blue, I= Indigo). Vì vậy, mẫu có thể có có thể là RGB, RGY, v.v.
Chúng ta hãy cùng xem hình sau:
Giải thích:
- Lấy 4 màu bất kỳ trong số 5 màu và liệt kê chúng
- Từ mỗi khối 4 màu, chọn 3 màu bất kỳ và liệt kê tất cả. Ví dụ: chúng tôi chỉ chọn “RGBI” trong hình và hiển thị 4 kết hợp.
- Có một lý thuyết đằng sau nó để tính tổng số kết hợp mà chúng ta có thể thực hiện. Sự kết hợp của r phần tử trong số n có thể được biểu diễn dưới dạng toán học:
Dấu hiệu "!" có nghĩa là những yếu tố. Ví dụ,
N! = N * (N-1) * (N-2) * … * 3 * 2 * 1
Nói, 5! = 5*4*3*2*1 = 120
Vì vậy, đối với bài toán trên, chúng ta có 5 màu nghĩa là n = 5 và tại bất kỳ thời điểm nào, chúng ta cần chọn 3 màu bất kỳ. Vậy r = 3. Sau khi tính toán, chúng ta nhận được:
Có thể kết hợp tổng cộng 10 màu cho kịch bản trên.
Phân tích độ phức tạp thời gian cho Kết hợp
Bây giờ, giả sử, cho một mảng có kích thước n, chúng ta được yêu cầu lấy r phần tử từ mảng và thực hiện kết hợp r phần tử.
Nếu cho trước một mảng có kích thước n thì nó sẽ mất O(n2) thời gian thực hiện nhiệm vụ. Ngoài ra, nếu chúng tôi muốn loại bỏ mục trùng lặp, thì:
Chúng ta cần thực hiện các bước sau:
Bước 1) Sắp xếp dữ liệu mảng đầu vào theo cách tăng dần. Độ phức tạp thời gian của việc sắp xếp là O(n*log(n)).
Bước 2) Tạo một mảng khác chứa phần tử duy nhất từ dữ liệu mảng tạm thời đã cho.
Bước 3) Sau đó, thực hiện chức năng kết hợp.
Vì vậy, độ phức tạp thời gian tổng thể trở thành = Trên2) + O(nLog(n)). Chúng ta có thể coi nó là O(n2), như n2 lớn hơn nhiều so với n*log(n).
Cách 1: Phần tử cố định có đệ quy
Trong phương pháp này, chúng tôi sẽ chọn một phần tử sau đó tìm sự kết hợp của các phần tử r-1. Khi chúng tôi chọn một phần tử từ phần tử còn lại, chúng tôi đang thực hiện đệ quy và đó là lý do tại sao nó được gọi là phần tử cố định và đệ quy.
Hãy trình diễn thuật toán từng bước bằng sơ đồ:
Các bước được đưa ra dưới đây:
Bước 1) Trong lớp đầu tiên, lấy các phần tử n-r+1. Có nghĩa là chúng tôi đã lấy 3 yếu tố.
Bước 2) Chọn một phần tử từ lớp thứ 2 và đưa nó lên nr. Vì vậy, nếu chúng ta lấy “R” thì với R, chúng ta có thể lấy G, Y và B.
Bước 3) Chọn một phần tử từ lớp thứ 3 và đưa nó lên phần tử thứ n và tạo thành các khối chứa 3 phần tử, mỗi khối.
Hình trên là giá trị trả về từ đệ quy. Chỉ có lớp cuối cùng sẽ được in.
Mã giả
function combination: pass in: inputArray, combinationArray, start, end, index, r if index is equal to r: for each element in combinationArray: print each element return for i = start: if i <=end and end -i+1 > r-index: combinationArray[index] = inputArray[i] call combination function again with updated parameter
Triển khai bằng C/C++
#include<bits/stdc++.h> #include<stdio.h> void Combination(char inputArray[], char combinationArray[], int start, int end, int index, int r) { if (index == r) { for (int i = 0; i & lt; r; i++) { printf("%c", combinationArray[i]); } printf("\n"); return; } for (int i = start; i & lt; = end & amp; & amp; end - i + 1 & gt; = r - index; i++) { combinationArray[index] = inputArray[i]; Combination(inputArray, combinationArray, i + 1, end, index + 1, r); } } int main() { char inputArray[] = {'R','G','Y','B','I'}; int n = sizeof(inputArray) / sizeof(inputArray[0]); int r = 3; char combinationArray[r]; printf("Combinations:\n"); Combination(inputArray, combinationArray, 0, n - 1, 0, r); }
Đầu ra:
Combinations: RGY RGB RGI RYB RYI RBI GYB GYI GBI YBI
Triển khai tại Python
def Combination(inputArray, combinationArray, start, end, index, r): if index == r: for item in combinationArray: print(item, end = " ") print() return i = start while (i & lt; = end and end - i + 1 & gt; = r - index): combinationArray[index] = inputArray[i] Combination(inputArray, combinationArray, i + 1, end, index + 1, r) i += 1 inputArray = "RGYBI" n = len(inputArray) r = 3 combinationArray = [0] * r Combination(inputArray, combinationArray, 0, n - 1, 0, r)
Đầu ra:
R G Y R G B R G I R Y B R Y I R B I G Y B G Y I G B I Y B I
Cách 2 (Bao gồm và loại trừ mọi phần tử)
Phương pháp này dựa trên bản sắc của Pascal. Trước đây chúng ta sử dụng đệ quy để tính nCr. Ở đây, phương pháp chỉ được chia thay vì một vòng lặp phức tạp.
Theo nhận dạng của Pascal,
nCr = (n-1)Cr + (n-1)C(r-1)
Vì vậy, sẽ có 2 logic đệ quy cho thuật toán đệ quy để tìm Tổ hợp r phần tử từ một mảng có kích thước n cho trước.
- Phần tử được bao gồm trong Kết hợp hiện tại
- Phần tử bị loại trừ khỏi Kết hợp hiện tại
Mã giả
function combination: pass in: inputArray, combinationArray, n, r, index, i if the index is equal to r: for each element in combination array: print each element if i>=n: return combinationArray[index] = inputArray[i] combination(inputArray, combinationArray, n, r, index+1, i+1) combination(inputArray, combinationArray, n, r, index, i+1)
Triển khai bằng C/C++
#include<bits/stdc++.h> #include<stdio.h> void Combination(char inputArray[], char combinationArray[], int n, int r, int index, int i) { if (index == r) { for (int j = 0; j & lt; r; j++) { printf("%c", combinationArray[j]); } printf("\n"); return; } if (i & gt; = n) return; combinationArray[index] = inputArray[i]; Combination(inputArray, combinationArray, n, r, index + 1, i + 1); Combination(inputArray, combinationArray, n, r, index, i + 1); } int main() { char inputArray[] = {'R','G','Y','B','I'}; int n = sizeof(inputArray) / sizeof(inputArray[0]); int r = 3; char combinationArray[r]; printf("Combinations:\n"); Combination(inputArray, combinationArray, n, r, 0, 0); }
Đầu ra:
Combinations: RGY RGB RGI RYB RYI RBI GYB GYI GBI YBI
Triển khai tại Python
def Combination(inputArray, combinationArray, n, r, index, i): if index == r: for item in combinationArray: print(item, end = " ") print() return if i & gt; = n: return combinationArray[index] = inputArray[i] Combination(inputArray, combinationArray, n, r, index + 1, i + 1); Combination(inputArray, combinationArray, n, r, index, i + 1); inputArray = "RGYBI" n = len(inputArray) r = 3 combinationArray = [""] * r Combination(inputArray, combinationArray, n, r, 0, 0)
Đầu ra:
R G Y R G B R G I R Y B R Y I R B I G Y B G Y I G B I Y B I
Xử lý các kết hợp trùng lặp
Đôi khi có thể có các phần tử trùng lặp trong mảng đầu vào.
Ví dụ,
- Mảng đầu vào chứa n = {5, 2, 3, 1, 5}.
- Ở đây, chúng ta có thể thấy rằng 5 xuất hiện 2 lần.
- Bây giờ, nếu chúng ta muốn chạy mã cho mảng này, một số kết hợp sẽ được lặp lại.
- Chúng tôi sẽ tìm thấy {5, 2, 5}, {5, 2, 3}, v.v. hoặc bất kỳ kết hợp nào có chứa 5 sẽ được lặp lại.
Chúng ta có thể sử dụng hai phương pháp sau:
- Sắp xếp mảng đầu vào. Việc sắp xếp sẽ mất thời gian O(nlog(n)).
- Sau đó tăng giá trị của i, trong khi giá trị i và giá trị i+1 là như nhau. Về cơ bản, hãy đặt hai dòng mã sau vào hàm Combination.
// For c/c++ while(inputArray[i] == inputArray[i+1]){ i++; }
# for python while inputArray[i]==inputArray[i+1]: i+=1
Sử dụng từ điển hoặc bản đồ không có thứ tự để theo dõi các kết hợp trùng lặp
Vì vậy, nếu không muốn sắp xếp các phần tử để theo dõi phần trùng lặp, chúng ta có thể làm theo các bước đã cho.
Bước 1) Khai báo một từ điển toàn cầu hoặc hashmap.
Bước 2) Đẩy Kết hợp đã tạo vào bản đồ băm và tăng giá trị lên một. Sự kết hợp là chìa khóa và sự xuất hiện của chúng là giá trị.
Bước 3) khi hàm chạy xong, chúng ta chỉ cần in tất cả các khóa từ hashmap hoặc từ điển.
Đây là cách triển khai trong python
unique_combination = dict() def Combination(inputArray, combinationArray, n, r, index, i): if index == r: temp_combination = "" for item in combinationArray: temp_combination += item unique_combination[temp_combination] = unique_combination.get(temp_combination, 0) + 1 return if i & gt; = n: return combinationArray[index] = inputArray[i] Combination(inputArray, combinationArray, n, r, index + 1, i + 1); Combination(inputArray, combinationArray, n, r, index, i + 1); inputArray = "RGYBIB" n = len(inputArray) r = 3 combinationArray = [""] * r Combination(inputArray, combinationArray, n, r, 0, 0) for item in unique_combination.keys(): print(item)
Đầu ra:
RGY RGB RGI RYB RYI RBI RBB RIB GYB GYI GBI GBB GIB YBI YBB YIB BIB
Tại đây, bạn có thể thấy đầu vào là “RGYBIB”. Nói chung, sẽ xảy ra một số kết hợp trùng lặp. Nhưng vì chúng tôi đã sử dụng từ điển và coi mỗi Kết hợp là khóa nên chúng tôi chỉ có thể in Kết hợp duy nhất.
Bây giờ, nếu bạn viết “print(unique_combination)”, bạn có thể thấy tần suất của từng kết hợp. Nó sẽ hiển thị như thế này:
{'RGY': 1, 'RGB': 2, 'RGI': 1, 'RYB': 2, 'RYI': 1, 'RBI': 1, 'RBB': 1, 'RIB': 1, 'GYB': 2, 'GYI': 1, 'GBI': 1, 'GBB': 1, 'GIB': 1, 'YBI': 1, 'YBB': 1, 'YIB': 1, 'BIB': 1}
Vì vậy, chúng ta có thể thấy RGB, RYB, GYB đã xảy ra 2 lần. Độ phức tạp thời gian của việc chèn khóa vào từ điển về cơ bản là O(1). Vì vậy, nếu bạn sử dụng từ điển, thì độ phức tạp thời gian tổng thể để chạy mã sẽ là:
O(1) + O(n*n)
Tương đương với O(n*n).
Sử dụng phương pháp trước để theo dõi trùng lặp, chúng ta cần O(n*log(n)) để sắp xếp; để so sánh, chúng ta cần O(n), và bản thân hàm mất O(n*n). Độ phức tạp thời gian tổng thể sẽ là:
O(n*log(n)) + O(n) +O(n*n)