Nghệ thuật lập trình máy tính Tập 4: Fascicle 3 có rất nhiều thứ có thể phù hợp với tình huống cụ thể của bạn hơn là cách tôi mô tả.
Mã màu xám
Tất nhiên, một vấn đề mà bạn sẽ gặp phải là bộ nhớ và khá nhanh, bạn sẽ gặp vấn đề với 20 yếu tố trong bộ của mình - 20 C 3 = 1140. Và nếu bạn muốn lặp lại bộ đó, tốt nhất nên sử dụng màu xám đã sửa đổi thuật toán mã để bạn không giữ tất cả chúng trong bộ nhớ. Chúng tạo ra sự kết hợp tiếp theo từ trước đó và tránh lặp lại. Có rất nhiều trong số này cho các mục đích sử dụng khác nhau. Chúng ta có muốn tối đa hóa sự khác biệt giữa các kết hợp liên tiếp không? giảm thiểu? vân vân.
Một số giấy tờ gốc mô tả mã màu xám:
- Một số đường dẫn Hamilton và thuật toán thay đổi tối thiểu
- Thuật toán tạo kết hợp trao đổi liền kề
Dưới đây là một số giấy tờ khác bao gồm chủ đề:
- Một triển khai hiệu quả của Eades, Hickey, Đọc thuật toán tạo kết hợp trao đổi liền kề (PDF, với mã trong Pascal)
- Máy phát điện kết hợp
- Khảo sát mã màu xám kết hợp (PostScript)
- Một thuật toán cho mã màu xám
Chase's Twiddle (thuật toán)
Phillip J Chase, ` Thuật toán 382: Sự kết hợp của M trong số N Đối tượng '(1970)
Thuật toán trong C ...
Chỉ số kết hợp theo thứ tự từ điển (Thuật toán Buckles 515)
Bạn cũng có thể tham chiếu một sự kết hợp theo chỉ số của nó (theo thứ tự từ điển). Nhận ra rằng chỉ mục nên có một số lượng thay đổi từ phải sang trái dựa trên chỉ mục, chúng ta có thể xây dựng một cái gì đó sẽ phục hồi kết hợp.
Vì vậy, chúng tôi có một bộ {1,2,3,4,5,6} ... và chúng tôi muốn có ba yếu tố. Giả sử {1,2,3} chúng ta có thể nói rằng sự khác biệt giữa các yếu tố là một và theo thứ tự và tối thiểu. {1,2,4} có một sự thay đổi và là số thứ tự từ điển 2. Vì vậy, số lượng 'thay đổi' ở nơi cuối cùng chiếm một sự thay đổi trong thứ tự null. Vị trí thứ hai, với một thay đổi {1,3,4} có một thay đổi nhưng chiếm nhiều thay đổi hơn vì nó ở vị trí thứ hai (tỷ lệ thuận với số lượng phần tử trong bộ gốc).
Phương pháp tôi đã mô tả là giải cấu trúc, vì dường như, từ thiết lập đến chỉ mục, chúng ta cần thực hiện ngược lại - khó hơn nhiều. Đây là cách Buckles giải quyết vấn đề. Tôi đã viết một số C để tính toán chúng , với những thay đổi nhỏ - Tôi đã sử dụng chỉ mục của các tập hợp thay vì một phạm vi số để biểu diễn tập hợp, vì vậy chúng tôi luôn làm việc từ 0 ... n. Ghi chú:
- Vì các kết hợp không có thứ tự, {1,3,2} = {1,2,3} - chúng tôi yêu cầu chúng là từ điển.
- Phương pháp này có 0 ẩn để bắt đầu thiết lập cho sự khác biệt đầu tiên.
Chỉ số kết hợp theo thứ tự từ điển (McCaffrey)
Có một cách khác :, khái niệm của nó dễ nắm bắt và lập trình hơn nhưng không có sự tối ưu hóa của Buckles. May mắn thay, nó cũng không tạo ra các kết hợp trùng lặp:
Tập hợp tối đa hóa , ở đâu .
Ví dụ : 27 = C(6,4) + C(5,3) + C(2,2) + C(1,1)
. Vì vậy, sự kết hợp từ vựng thứ 27 của bốn điều là: {1,2,5,6}, đó là các chỉ mục của bất kỳ tập hợp nào bạn muốn xem xét. Ví dụ dưới đây (OCaml), yêu cầu choose
chức năng, để lại cho người đọc:
(* this will find the [x] combination of a [set] list when taking [k] elements *)
let combination_maccaffery set k x =
(* maximize function -- maximize a that is aCb *)
(* return largest c where c < i and choose(c,i) <= z *)
let rec maximize a b x =
if (choose a b ) <= x then a else maximize (a-1) b x
in
let rec iterate n x i = match i with
| 0 -> []
| i ->
let max = maximize n i x in
max :: iterate n (x - (choose max i)) (i-1)
in
if x < 0 then failwith "errors" else
let idxs = iterate (List.length set) x k in
List.map (List.nth set) (List.sort (-) idxs)
Một vòng lặp kết hợp nhỏ và đơn giản
Hai thuật toán sau được cung cấp cho mục đích giáo khoa. Họ thực hiện một kết hợp tổng thể lặp và thư mục (tổng quát hơn). Chúng càng nhanh càng tốt, có độ phức tạp O ( n C k ). Tiêu thụ bộ nhớ bị ràng buộc bởi k
.
Chúng ta sẽ bắt đầu với iterator, sẽ gọi hàm do người dùng cung cấp cho mỗi kết hợp
let iter_combs n k f =
let rec iter v s j =
if j = k then f v
else for i = s to n - 1 do iter (i::v) (i+1) (j+1) done in
iter [] 0 0
Một phiên bản tổng quát hơn sẽ gọi hàm do người dùng cung cấp cùng với biến trạng thái, bắt đầu từ trạng thái ban đầu. Vì chúng ta cần chuyển trạng thái giữa các trạng thái khác nhau, chúng tôi sẽ không sử dụng vòng lặp for, nhưng thay vào đó, hãy sử dụng đệ quy,
let fold_combs n k f x =
let rec loop i s c x =
if i < n then
loop (i+1) s c @@
let c = i::c and s = s + 1 and i = i + 1 in
if s < k then loop i s c x else f c x
else x in
loop 0 0 [] x