Tôi đã chạy qua câu hỏi này trong khi nghiên cứu một vấn đề tương tự: bổ sung tối ưu chất lỏng để giảm sự phân tầng. Có vẻ như giải pháp của tôi cũng sẽ được áp dụng cho tình huống của bạn.
Nếu bạn muốn trộn chất lỏng A, B và C theo tỷ lệ 30,20,10 (nghĩa là 30 đơn vị A, 20 đơn vị B và 10 đơn vị C), bạn kết thúc bằng phân tầng nếu bạn thêm tất cả A, sau đó là tất cả B, và sau đó là tất cả C. Tốt hơn hết là trộn các đơn vị nhỏ hơn. Ví dụ: thực hiện bổ sung đơn vị trong chuỗi [A, B, A, C, B, A]. Điều đó sẽ ngăn chặn sự phân tầng hoàn toàn.
Cách tôi tìm thấy để làm điều đó là coi nó như một loại hợp nhất, sử dụng hàng đợi ưu tiên. Nếu tôi tạo một cấu trúc để mô tả các bổ sung:
MergeItem
Item, Count, Frequency, Priority
Tần số được biểu thị là "một mỗi N". Vì vậy, A, được thêm ba trong sáu lần, có tần số là 2 (6/3).
Và khởi tạo một đống có chứa ban đầu:
(A, 3, 2, 2)
(B, 2, 3, 3)
(C, 1, 6, 6)
Bây giờ, tôi loại bỏ mục đầu tiên từ heap và xuất nó. Sau đó giảm số lượng của nó đi 1 và tăng mức độ ưu tiên theo tần số và thêm nó trở lại vào heap. Heap kết quả là:
(B, 2, 3, 0)
(A, 2, 2, 4)
(C, 1, 6, 6)
Tiếp theo, xóa B khỏi heap, xuất và cập nhật nó, sau đó thêm lại vào heap:
(A, 2, 2, 4)
(C, 1, 6, 6)
(B, 1, 3, 6)
Nếu tôi tiếp tục theo cách đó, tôi sẽ có được hỗn hợp mong muốn. Tôi sử dụng một bộ so sánh tùy chỉnh để đảm bảo rằng khi các mục Ưu tiên bằng nhau được chèn vào heap, thì mục có giá trị Tần số cao nhất (nghĩa là ít thường xuyên nhất) được đặt hàng trước.
Tôi đã viết một mô tả đầy đủ hơn về vấn đề và giải pháp của nó trên blog của mình và trình bày một số mã C # đang hoạt động minh họa nó. Xem các mục phân phối đồng đều trong một danh sách .
Cập nhật sau khi bình luận
Tôi nghĩ vấn đề của tôi tương tự như vấn đề của OP, và do đó giải pháp của tôi có khả năng hữu ích. Tôi xin lỗi vì đã không đóng khung câu trả lời của tôi nhiều hơn trong các điều khoản của câu hỏi của OP.
Sự phản đối đầu tiên, rằng giải pháp của tôi là sử dụng A, B và C thay vì 0, 1 và 2, dễ dàng được khắc phục. Nó đơn giản chỉ là vấn đề danh pháp. Tôi thấy dễ dàng hơn và ít bối rối hơn khi nghĩ về và nói "hai A" thay vì "hai 1". Nhưng vì mục đích của cuộc thảo luận này, tôi đã sửa đổi các kết quả đầu ra dưới đây để sử dụng danh pháp của OP.
Tất nhiên vấn đề của tôi liên quan đến khái niệm khoảng cách. Nếu bạn muốn "trải đều mọi thứ", khoảng cách được ngụ ý. Nhưng, một lần nữa, đó là thất bại của tôi vì đã không thể hiện đầy đủ vấn đề của tôi tương tự như vấn đề của OP.
Tôi đã chạy một vài thử nghiệm với hai ví dụ mà OP cung cấp. Đó là:
[1,1,2,2,3,3] // which I converted to [0,0,1,1,2,2]
[0,0,0,0,1,1,1,2,2,3]
Trong danh pháp của tôi, chúng được biểu thị lần lượt là [2,2,2] và [4,3,2,1]. Đó là, trong ví dụ cuối cùng, "4 mục loại 0, 3 mục loại 1, 2 mục loại 2 và 1 mục loại 3."
Tôi đã chạy chương trình thử nghiệm của mình (như được mô tả ngay bên dưới) và đã đăng kết quả của mình. Không có đầu vào từ OP, tôi không thể nói nếu kết quả của tôi tương tự, tệ hơn, hoặc tốt hơn so với kết quả của anh ấy. Tôi cũng không thể so sánh kết quả của mình với kết quả của bất kỳ ai khác vì không ai khác đã đăng bất kỳ.
Tuy nhiên, tôi có thể nói rằng thuật toán cung cấp một giải pháp tốt cho vấn đề của tôi về việc loại bỏ sự phân tầng khi trộn chất lỏng. Và có vẻ như nó cung cấp một giải pháp hợp lý cho vấn đề của OP.
Đối với các kết quả được hiển thị bên dưới, tôi đã sử dụng thuật toán mà tôi đã nêu chi tiết trong mục nhập blog của mình, với mức ưu tiên ban đầu được đặt thành Frequency/2
và bộ so sánh heap được sửa đổi để ưu tiên cho mục thường xuyên hơn. Mã sửa đổi được hiển thị ở đây, với các dòng sửa đổi nhận xét.
private class HeapItem : IComparable<HeapItem>
{
public int ItemIndex { get; private set; }
public int Count { get; set; }
public double Frequency { get; private set; }
public double Priority { get; set; }
public HeapItem(int itemIndex, int count, int totalItems)
{
ItemIndex = itemIndex;
Count = count;
Frequency = (double)totalItems / Count;
// ** Modified the initial priority setting.
Priority = Frequency/2;
}
public int CompareTo(HeapItem other)
{
if (other == null) return 1;
var rslt = Priority.CompareTo(other.Priority);
if (rslt == 0)
{
// ** Modified to favor the more frequent item.
rslt = Frequency.CompareTo(other.Frequency);
}
return rslt;
}
}
Chạy chương trình thử nghiệm của tôi với ví dụ đầu tiên của OP, tôi nhận được:
Counts: 2,2,2
Sequence: 1,0,2,1,0,2
Distances for item type 0: 3,3
Stddev = 0
Distances for item type 1: 3,3
Stddev = 0
Distances for item type 2: 3,3
Stddev = 0
Vì vậy, thuật toán của tôi hoạt động cho bài toán tầm thường của tất cả các số bằng nhau.
Đối với vấn đề thứ hai mà OP đã đăng, tôi nhận được:
Counts: 4,3,2,1
Sequence: 0,1,2,0,1,3,0,2,1,0
Distances for item type 0: 3,3,3,1
Stddev = 0.866025403784439
Distances for item type 1: 3,4,3
Stddev = 0.471404520791032
Distances for item type 2: 5,5
Stddev = 0
Distances for item type 3: 10
Stddev = 0
Standard dev: 0.866025403784439,0.471404520791032,0,0
Tôi không thấy một cách rõ ràng để cải thiện điều đó. Nó có thể được sắp xếp lại để tạo khoảng cách cho mục 0 [2,3,2,3] hoặc một số cách sắp xếp khác của 2 và 3, nhưng điều đó sẽ thay đổi độ lệch cho mục 1 và / hoặc 2. Tôi thực sự không biết điều gì "tối ưu" là trong tình huống này. Là tốt hơn để có một độ lệch lớn hơn trên thường xuyên hơn hoặc trên các mặt hàng ít thường xuyên hơn?
Thiếu các vấn đề khác từ OP, tôi đã sử dụng các mô tả của anh ấy để tạo ra một vài vấn đề của riêng tôi. Ông nói trong bài viết của mình:
Một danh sách điển hình có ~ 50 mặt hàng với ~ 15 giá trị khác nhau với số lượng khác nhau.
Vì vậy, hai bài kiểm tra của tôi là:
[8,7,6,5,5,4,3,3,2,2,2,1,1,1,1] // 51 items, 15 types
[12,6,5,4,4,3,3,3,2,2,2,1,1] // 48 items, 13 types
Và kết quả của tôi:
Counts: 8,7,6,5,5,4,3,3,2,2,2,1,1,1,1
Sequence: 0,1,2,3,4,5,7,6,0,1,2,8,9,10,4,3,0,1,5,2,0,1,3,4,6,7,14,11,13,12,0,2,5,1,0,3,4,2,8,10,9,1,0,7,6,5,3,4,2,1,0
Distances for item type 0: 8,8,4,10,4,8,8,1
Stddev = 2.82566363886433
Distances for item type 1: 8,8,4,12,8,8,3
Stddev = 2.76272565797339
Distances for item type 2: 8,9,12,6,11,5
Stddev = 2.5
Distances for item type 3: 12,7,13,11,8
Stddev = 2.31516738055804
Distances for item type 4: 10,9,13,11,8
Stddev = 1.72046505340853
Distances for item type 5: 13,14,13,11
Stddev = 1.08972473588517
Distances for item type 6: 17,20,14
Stddev = 2.44948974278318
Distances for item type 7: 19,18,14
Stddev = 2.16024689946929
Distances for item type 8: 27,24
Stddev = 1.5
Distances for item type 9: 28,23
Stddev = 2.5
Distances for item type 10: 26,25
Stddev = 0.5
Distances for item type 11: 51
Stddev = 0
Distances for item type 12: 51
Stddev = 0
Distances for item type 13: 51
Stddev = 0
Distances for item type 14: 51
Stddev = 0
Và cho ví dụ thứ hai:
Counts: 12,6,5,4,4,3,3,3,2,2,2,1,1
Sequence: 0,1,2,0,3,4,7,5,6,0,1,8,9,10,0,2,0,3,4,1,0,2,6,7,5,12,11,0,1,0,3,4,2,0,1,10,8,9,0,7,5,6,0,
4,3,2,1,0
Distances for item type 0: 3,6,5,2,4,7,2,4,5,4,5,1
Stddev = 1.68325082306035
Distances for item type 1: 9,9,9,6,12,3
Stddev = 2.82842712474619
Distances for item type 2: 13,6,11,13,5
Stddev = 3.44093010681705
Distances for item type 3: 13,13,14,8
Stddev = 2.34520787991171
Distances for item type 4: 13,13,12,10
Stddev = 1.22474487139159
Distances for item type 5: 17,16,15
Stddev = 0.816496580927726
Distances for item type 6: 14,19,15
Stddev = 2.16024689946929
Distances for item type 7: 17,16,15
Stddev = 0.816496580927726
Distances for item type 8: 25,23
Stddev = 1
Distances for item type 9: 25,23
Stddev = 1
Distances for item type 10: 22,26
Stddev = 2
Distances for item type 11: 48
Stddev = 0
Distances for item type 12: 48
Stddev = 0