Thuật toán để phân phối các mặt hàng


25

Tôi đang tìm kiếm một thuật toán để phân phối các giá trị từ một danh sách để danh sách kết quả là "cân bằng" hoặc "phân bổ đều" nhất có thể (trong ngoặc kép vì tôi không chắc đây là những cách tốt nhất để mô tả nó ... sau này tôi sẽ cung cấp một cách để đo lường nếu kết quả tốt hơn kết quả khác).

Vì vậy, cho danh sách:

[1, 1, 2, 2, 3, 3]

Một trong những kết quả tốt nhất, sau khi phân phối lại các giá trị, là:

[1, 2, 3, 1, 2, 3]

Có thể có kết quả khác tốt như kết quả này, và tất nhiên điều này trở nên phức tạp hơn với một bộ giá trị ít đồng nhất hơn.

Đây là cách đo lường nếu kết quả tốt hơn kết quả khác:

  1. Đếm khoảng cách giữa mỗi mục và mục tiếp theo có cùng giá trị.

  2. Tính độ lệch chuẩn cho khoảng cách đó. Một sự phân tán thấp hơn có nghĩa là một kết quả tốt hơn.

Quan sát:

  • Khi tính toán khoảng cách và kết thúc danh sách mà không tìm thấy mục nào có cùng giá trị, chúng tôi quay lại phần đầu của danh sách. Vì vậy, nhiều nhất, cùng một mục sẽ được tìm thấy và khoảng cách cho mục đó sẽ là chiều dài của danh sách. Điều này có nghĩa là danh sách theo chu kỳ ;
  • 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ì thế:

  • Đối với kết quả [1, 2, 3, 1, 2, 3], khoảng cách là [3, 3, 3, 3, 3, 3], và độ lệch chuẩn là 0;
  • Đối với kết quả [1, 1, 2, 2, 3, 3], khoảng cách là [1, 5, 1, 5, 1, 5], và độ lệch chuẩn là 2;
  • Điều này làm cho kết quả đầu tiên tốt hơn kết quả thứ hai (độ lệch thấp hơn là tốt hơn).

Đưa ra các định nghĩa này, tôi yêu cầu một manh mối về những thuật toán hoặc chiến lược nào tôi nên tìm kiếm.


Có vẻ như bạn muốn giải quyết (biến thể tối ưu hóa của vấn đề ) Phân vùng , ít nhất là gần đúng. Có lẽ có nhiều thuật toán cho cái đó!
Raphael

Đọc lại điều này, tại sao việc đếm các lần xuất hiện của tất cả các giá trị và sau đó đặt các giá trị theo chu kỳ không phải lúc nào cũng mang lại giải pháp tối ưu?
Raphael

Câu trả lời:


8

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/2và 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

@DW Xin vui lòng xem cập nhật của tôi. Tôi tin rằng tôi cho thấy vấn đề của tôi tương tự như vấn đề của OP và cách thuật toán của tôi cung cấp giải pháp cho vấn đề của OP.
Jim Mischel

Đồ tốt! Cảm ơn đã cập nhật tuyệt vời. Nâng cao.
DW

Khá thú vị, như tôi đã nói trước đây. Sự đơn giản của ý tưởng là hấp dẫn. Tôi không có thời gian để đọc tất cả một cách cẩn thận. Giải pháp của bạn có thực sự tính đến tính chu kỳ của câu hỏi ban đầu không? Có thể có một cách để điều chỉnh nó cho mục đích này, nhưng tôi không hoàn toàn chắc chắn.
babou

@babou: Các tính toán khoảng cách của tôi thực hiện xung quanh, như bạn có thể thấy trong kết quả, nhưng bản thân thuật toán không tạo ra bất kỳ khoản phụ cấp cụ thể nào cho tính chất chu kỳ của vấn đề OP. Tôi cũng không thấy bất kỳ cách nào mà tôi có thể điều chỉnh thuật toán để làm như vậy. Hoặc, đối với vấn đề đó, việc tính đến tính chu kỳ sẽ cải thiện kết quả như thế nào. Mặc dù thật thú vị khi xem xét nhân đôi tất cả các tổng số (tức là thay đổi [3,2,1] thành [6,4,2]), đây sẽ là điều tương tự. Sự nghi ngờ của tôi là thuật toán sẽ tạo ra kết quả giống hệt nhau.
Jim Mischel

6

"Mùi" này giống như nó có thể là NP-hard. Vậy, bạn sẽ làm gì khi gặp vấn đề NP-hard? Ném một heuristic vào nó, hoặc một thuật toán gần đúng, hoặc sử dụng một bộ giải SAT.

Trong trường hợp của bạn, nếu bạn không cần giải pháp tối ưu tuyệt đối, một điểm khởi đầu hợp lý có thể là thử ủ mô phỏng . Có một cách tự nhiên để lấy bất kỳ giải pháp ứng cử viên nào và chuyển nó sang một giải pháp ứng viên gần đó: chọn ngẫu nhiên hai mục trong danh sách và trao đổi chúng. Ủ mô phỏng sẽ lặp đi lặp lại để cố gắng cải thiện giải pháp. Bạn có thể tìm thấy nhiều tài nguyên về mô phỏng ủ, nếu bạn không quen với nó. Bạn cũng có thể thử nghiệm với các "bước di chuyển cục bộ" khác để tạo ra những thay đổi nhỏ cho giải pháp ứng cử viên, với hy vọng cải thiện dần dần (nghĩa là giảm độ lệch chuẩn của khoảng cách).

ttt2xi,jxi,jijt2

Nhưng tôi sẽ đề nghị bạn bắt đầu với ủ mô phỏng. Đó là điều đầu tiên tôi sẽ thử, vì tôi nghĩ nó có thể hoạt động.


Là đề xuất của bạn là cách tiêu chuẩn để giải quyết các loại vấn đề lập kế hoạch. Tôi đoán có một số phần mềm thương mại xung quanh cho điều này. Làm thế nào để họ xử lý nó?
babou

@babou, câu hỏi tuyệt vời - Tôi không có ý kiến!
DW

Tôi tiếp tục phát triển các chi tiết về thuật toán của mình, nhưng tôi nghi ngờ rất nhiều ứng dụng hiện có sẽ sử dụng điều đó. Trên thực tế, tôi thậm chí tự hỏi liệu các ứng dụng lập lịch trình đối phó với một vấn đề thuộc loại này. Tôi đã hỏi thông tin về SE.softwarerecs, vì tôi không thấy cách đặt câu hỏi ở đây, ngoài nhận xét như tôi vừa làm.
babou

Các tối ưu giải pháp có thể là NP-hard. Nhưng một giải pháp khá khả thi là O (n log k), trong đó n là tổng số mục và k là số loại mục. Xem câu trả lời của tôi, và bài viết trên blog được liên kết của tôi.
Jim Mischel

2

Phác thảo thuật toán heuristic

Tôi không có giải pháp chính xác cho vấn đề này. Nhưng vì nhận xét của Raphael cho thấy nó giống như vấn đề phân vùng, mà thuật toán heuristic đã được phát triển, tôi sẽ thử một cách tiếp cận heuristic. Đây chỉ là một bản phác thảo của một thuật toán heuristic.

vn[1..n]ini

nvnvn/nv

v

in/ninmodnin/ni

Điều đó sẽ hướng dẫn thuật toán của chúng tôi.

n

i|n/niv|

Nó có thể là một giá trị với rất nhiều lần xuất hiện đầu tiên. Tôi nghĩ rằng nó không thực sự tạo ra sự khác biệt, vì các ràng buộc được tạo bằng cách chiếm các vị trí nằm trong tỷ lệ của số lượng giá trị (?) Được đặt.

Thr giá trị đầu tiên được xem xét có thể được đặt mà không có bất kỳ ràng buộc. Sau đó, các giá trị khác phải được đặt để giảm thiểu đóng góp của chúng vào độ lệch chuẩn, nhưng chỉ trong các vị trí còn trống bởi bất kỳ giá trị nào đã được đặt trước đó.

Việc đặt các lần xuất hiện của một giá trị trong các vị trí còn lại có thể được thực hiện bằng thuật toán lập trình động, để hợp nhất các phép tính đặt cùng một số giá trị giữa hai vị trí, chỉ giữ các giá trị đóng góp tối thiểu cho độ lệch chuẩn (nghĩa là giá trị tối thiểu cho tổng bình phương độ lệch của chúng).

v

j|n/njv|

Sau đó, bạn đặt các giá trị singleton trong các khe còn lại.

Tôi tin rằng điều này thường sẽ đưa ra giải pháp hợp lý, nhưng tôi chưa có ý tưởng nào về cách chứng minh hoặc ước tính khoảng cách với một giải pháp tối ưu.


Tôi có cùng một ấn tượng rằng không có vấn đề gì nếu chúng ta bắt đầu với những thứ phổ biến nhất hoặc ít phổ biến nhất, để các singletons sang một bên. Chiến lược rõ ràng mang lại cho tôi kết quả tốt nhất bắt đầu sắp xếp các giá trị theo sự xuất hiện và sắp xếp chúng theo thứ tự bắt đầu từ những giá trị xảy ra nhiều nhất. Điều này tự nhiên để lại singletons đến cuối cùng.
moraes

vn/vV

Bạn có nghĩa là, đối với một danh sách có 10 giá trị [0, 0, 0, 0, 1, 1, 1, 2, 2, 3]và v 4, chúng ta sẽ đặt các giá trị đầu tiên 1( 10/3 = 3.33, gần nhất với v), sau đó 2( 10/2 = 5, gần nhất tiếp theo), sau đó 0( 10/4 = 2.5)? Hoặc: bạn có thể đưa ra một ví dụ về "giảm độ lệch trung bình của khoảng cách từ giá trị v" không?
moraes

1
Không, tôi làm ngược lại. Lấy ví dụ của bạn, thứ tự định vị trước tiên là O vì khoảng cách trung bình của nó 2,5 sai lệch nhiều nhất so với v = 4, sau đó 2, và 1 và đơn lẻ 3. - - - Có phải bạn đang gợi ý rằng tôi nên viết lại rõ ràng hơn một số một phần của lời giải thích của tôi cho chiến lược này?
babou

Không, nó ổn. Tôi sẽ thử một cái gì đó theo ý tưởng này và báo cáo lại.
moraes

1

Có vẻ như tôi đến bữa tiệc rất muộn, nhưng đăng trong trường hợp bất cứ ai gặp lại chuyện này. Giải pháp của tôi tương tự như cộng với @ babou. Đầu ngày hôm nay, tôi đã có một vấn đề lập lịch trình trong một hệ thống nhúng dẫn tôi đến chủ đề này. Tôi có một triển khai cụ thể cho vấn đề của mình trong C, nhưng tôi nghĩ rằng tôi sẽ đăng một giải pháp chung hơn trong Python ở đây (phiên bản C rất phức tạp bởi thực tế là tôi đã giới hạn bản thân trong một ngăn xếp kích thước nhỏ, cố định và không có bộ nhớ phân bổ, vì vậy tôi thực hiện toàn bộ thuật toán tại chỗ). Kỹ thuật khử răng cưa được sử dụng dưới đây là thứ bạn có thể sử dụng để vẽ một đường trên màn hình với màu 2 bit. Thuật toán ở đây đạt được điểm thấp hơn (nghĩa là tốt hơn) khi được đo bằng cách sử dụng tổng độ lệch chuẩn cho các đầu vào được Jim Mischel sử dụng so với giải pháp cụ thể đó.

def generate(item_counts):
'''item_counts is a list of counts of "types" of items. E.g., [3, 1, 0, 2] represents
   a list containing [1, 1, 1, 2, 4, 4] (3 types of items/distinct values). Generate
   a new list with evenly spaced values.'''
# Sort number of occurrences by decreasing value.
item_counts.sort(reverse=True)
# Count the total elements in the final list.
unplaced = sum(item_counts)
# Create the final list.
placements = [None] * unplaced

# For each type of item, place it into the list item_count times.
for item_type, item_count in enumerate(item_counts):
    # The number of times the item has already been placed
    instance = 0
    # Evenly divide the item amongst the remaining unused spaces, starting with
    # the first unused space encountered.
    # blank_count is the number of unused spaces seen so far and is reset for each
    # item type.
    blank_count = -1
    for position in range(len(placements)):
        if placements[position] is None:
            blank_count += 1
            # Use an anti-aliasing technique to prevent bunching of values.
            if blank_count * item_count // unplaced == instance:
                placements[position] = item_type
                instance += 1
    # Update the count of number of unplaced items.
    unplaced -= item_count

return placements

Kết quả cho

Input counts: 8,7,6,5,5,4,3,3,2,2,2,1,1,1,1
Sum of stddev: 16.8 (vs. 22.3 via Jim Mischel)

Input of counts: 12,6,5,4,4,3,3,3,2,2,2,1,1
Sum of stddev: 18.0 (vs. 19.3 via Jim Mischel)

Nếu các đầu vào có dạng được chỉ định bởi @moraes, người ta có thể chuyển đổi nó thành dạng có thể sử dụng được bằng chức năng này trong các bước O (n) bằng cách sử dụng các bit của bộ nhớ Big Omega (n * log (n)) trong đó n là số lượng vật phẩm ( trong danh sách có 255 phần tử, bạn sẽ không cần nhiều hơn 255 byte) bằng cách giữ một mảng song song với số lần lặp lại. Thay phiên, người ta có thể thực hiện một cặp sắp xếp tại chỗ với bộ nhớ phụ O (1).

PS

import numpy
import collections

def evaluate(l):
    '''Given a distribution solution, print the sum of stddevs for each type in the solution.'''
    l2 = l * 2
    distances = [None] * len(l)
    distance_dict = collections.defaultdict(list)
    for i in range(len(l)):
        distances[i] = l2.index(l[i], i + 1) - i
        distance_dict[l[i]].append(l2.index(l[i], i + 1) - i)

    keys = list(distance_dict.keys())
    keys.sort()
    score = 0
    # Calculate standard deviations for individual types.
    for key in keys:
        sc = numpy.std(distance_dict[key])
        score += sc
    print('Stddev sum: ', score)

Chỉnh sửa: Tôi biết giải pháp này không tạo ra đầu ra tối ưu bằng ví dụ mẫu. Một đầu vào của [6, 2, 1]sản xuất [0, 1, 0, 0, 2, 0, 0, 1, 0]; một giải pháp tốt hơn là [0, 0, 1, 0, 2, 0, 0, 1, 0].


Tôi tin rằng tôi đã giải thích thuật toán của mình trong các nhận xét mã và cơ sở cho thuật toán trong phần mở đầu.
lungj

Tôi muốn thấy một mô tả độc lập về các ý tưởng đằng sau thuật toán của bạn và mã giả ngắn gọn cho thuật toán. Hiện tại những gì tôi thấy trong văn bản giới thiệu là (1) cách tiếp cận của bạn tương tự như @ babou và (2) nó sử dụng kỹ thuật khử răng cưa (bằng cách nào đó). Ngoài ra, không phải ai ở đây cũng đọc Python. Trong mọi trường hợp, đó là một câu trả lời cũ, vì vậy tôi hiểu nếu bạn không muốn cải thiện nó, nhưng tôi chỉ lưu ý những kỳ vọng của chúng tôi trên trang web này - không chỉ cho bạn, mà cho những người khác có thể chạy trên trang này trong tương lai và có khuynh hướng trả lời.
DW

0

Thuật toán này hoạt động với một mảng các số nguyên, trong đó mỗi số nguyên biểu thị một loại khác nhau. Nó tạo ra các mảng riêng cho từng loại. Ví dụ: nếu mảng bắt đầu là [1, 1, 1, 2, 2, 3], nó sẽ tạo ra ba mảng, [3], [2, 2], [1, 1, 1].

Từ đó, nó kết hợp đệ quy hai mảng nhỏ nhất (trong ví dụ này là [3] và [2,2]) và sắp xếp vị trí của các phần tử của mảng nhỏ hơn vào mảng nhỏ thứ hai chủ yếu dựa trên tỷ lệ của số sự xuất hiện của lớn hơn so với các loại nhỏ hơn. Trong ví dụ này, chúng tôi sẽ kết thúc với [2,3,2]. Sau đó, nó sẽ sử dụng mảng này làm mảng nhỏ hơn sẽ được kết hợp thành mảng lớn hơn tiếp theo, cho đến khi chỉ còn một mảng.

<?php
/**
 *This will separete the source array into separate arrays for each category
 */
function splitArrayByCategory($source) {

    //  index is the category, value is the tally
    $categoryCounts  = array_count_values($source);

    // Sort that list, keep index associations
    asort($categoryCounts);

    // build separate arrays for each category
    // index = order, smaller index = smaller category count
    // value = array of each category
    $separated = array();
    foreach ($categoryCounts as $category => $tally)
        $separated[] = array_fill(0, $tally, $category);

    return $separated;
}

/**
 * Will take the array of arrays generated by splitArrayByCategory, and merge
 * them together so categories are evenly distributed
 */
function mergeCategoryArrays($categoryArray) {

    // How many entries are there, for the smallest & second smallest categories
    $smallerCount = count($categoryArray[0]);
    $largerCount  = count($categoryArray[1]);

    // Used to determine how much space there should be between these two categories
    $space = $largerCount/$smallerCount;

    // Merge the array of the smallest category into the array of the second smallest
    foreach ($categoryArray[0] as $domIndex => $domain) {
        // Where should we splice the value into the second array?
        $location = floor($domIndex*$space+$domIndex+($space/2));
        // actually perform the splice
        array_splice($categoryArray[1], $location, 0, $domain);
    }

    // remove the smallest domain we just spliced into the second smallest
    unset($categoryArray[0]);

    // reset the indexes
    $categoryArray = array_values($categoryArray);

    // If we have more than one index left in the categoryArray (i.e. some things
    // still need to get merged), let's re-run this function,
    if (count($categoryArray)>1)
        $categoryArray = mergeCategoryArrays($categoryArray);

    return $categoryArray;
}

// The sample list we're working with.
// each integer represents a different category
$listSample = [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,6,6,7,7,7,7];

// Split the sample list into separate arrays for each category
$listSplitByCategory = splitArrayByCategory($listSample);

// If there are not at least two categories, what's the point?
if (count($listSplitByCategory) < 2) throw new Exception("You need at least two categories");

// perform the actual distribution of categories.
$listEvenlyDistributed = mergeCategoryArrays($listSplitByCategory)[0];

// Display the array before and after the categories are evenly distributed
for ($i=0; $i<count($listSample); $i++) {
    print $listSample[$i].",";
    print $listEvenlyDistributed[$i]."\n";
}

2
Đây không phải là một trang web mã hóa. Xin vui lòng không đăng câu trả lời chỉ mã. Thay vào đó, chúng tôi muốn bạn giải thích các ý tưởng đằng sau câu trả lời của bạn và cung cấp mã giả ngắn gọn cho thuật toán của bạn.
DW

Chào mừng bạn đến với Khoa học máy tính ! Chỉ trong trường hợp bạn không nhận thức được hoặc bạn quên mất một lúc, đọc mã bằng một ngôn ngữ cụ thể thường là một trong những nhiệm vụ khó khăn nhất mà chúng ta có thể có, đôi khi ngay cả khi mã được viết bởi chính chúng ta. Đó là một phần lý do tại sao chúng tôi không đánh giá cao mã thực sự trên trang web này, mặc dù nó có thể đại diện cho công việc nhiều hơn so với mã giả được viết lỏng lẻo. Tất nhiên, tôi đánh giá cao tất cả các mã làm việc thực tế có thể được chạy hoặc lấp lánh ngay lập tức.
Apass.Jack

Lời giải thích là có. trong mã trình diễn nhận xét; mà không phải trong một số cú pháp cổ xưa như APL, nhưng một cú pháp dễ hiểu đủ gần với mã giả. Nó sẽ giúp nếu lời giải thích của tôi không phải là phông chữ đơn cách?
vtim

Vâng. Nó giúp. Không phải ai cũng đọc PHP, có lẽ không phải ai cũng có thể xác định đâu là bình luận (có thể đó là đối số của người rơm) hoặc đơn giản là không muốn đọc khối mã và giải thích nó, nhưng hãy đọc ý tưởng mà bạn đã đưa vào đầu và nó nói lên tất cả +1 từ tôi. Mã của bạn sạch sẽ và được ghi chép tốt, nhưng chúng tôi chỉ đơn giản là không mã hóa trang web, vì vậy mô tả văn bản rất quan trọng ở đây. Cảm ơn bạn đã chỉnh sửa của bạn.
Ác ma

-1

Mã số ANSI C

Mã này hoạt động bằng cách tưởng tượng một đường thẳng trong không gian n chiều (trong đó n là số lượng danh mục) đi qua gốc tọa độ với vectơ chỉ hướng (v1, v2, ..., vi, ... vn) trong đó vi là số các mục trong danh mục i. Bắt đầu từ điểm gốc, mục đích là tìm điểm gần nhất tiếp theo với đường thẳng. Sử dụng ví dụ [0 0 0 0 0 1 1 1 2 2 2 3] nó tạo ra kết quả [0 1 2 0 3 1 0 2 0 1 2 0]. Sử dụng ví dụ của Lungj [0 0 0 0 0 0 1 1 2] chúng tôi nhận được [0 1 0 0 2 0 0 1 0], hoàn toàn giống với kết quả của Lungj.

Thuật toán được thực hiện hiệu quả hơn bằng cách chỉ sử dụng số học số nguyên và chỉ xem xét các vùng đồng bằng giữa các khoảng cách từ mỗi điểm đến đường thẳng.

#define MAXCATEGORIES 100

int chính () {int i = 0; int j = 0; int catize = 0; int vector [MAXCATEGORIES]; điểm int [MAXCATEGORIES]; int loại = 0; int Totalitems = 0; int tốt nhất = 0; dài d2 = 0L; dài vp = 0L; dài v2 = 0L; đồng bằng dài = 0L; beta dài = 0L;

printf("Enter the size of each category (enter 0 to finish):\r\n");
do
{
    catsize = 0;
    #ifdef _MSVC_LANG
            scanf_s("%d", &catsize);
    #else
            scanf("%d", &catsize)
    #endif
    if (catsize > 0)
    {
        vector[categories] = catsize;
        totalitems += catsize;
        categories++;
    }
} while (catsize > 0);

for (i = 0; i < categories; i++)
{
    v2 += vector[i] * vector[i];
    point[i] = 0;
}

for (i = 0; i < totalitems; i++)
{
    for (j = 0; j < categories; j++)
    {
        delta = (2 * point[j] + 1)*v2 - (2 * vp + vector[j])*vector[j];
        if (j == 0 || delta < beta)
        {
            best = j;
            beta = delta;
        }
    }
    point[best]++;
    vp += vector[best];
    printf("%c ", best + '0');  // Change '0' to 'A' if you like letters instead
}
return 0;

}


1
Chào mừng đến với trang web! Định dạng khôn ngoan, bạn cần thụt lề từng dòng mã của mình với bốn khoảng trắng để hệ thống được đánh dấu đúng. Nói chung, chúng tôi không tìm kiếm các khối mã lớn như câu trả lời cho các câu hỏi và đặc biệt, thói quen nhập dữ liệu của bạn không thêm bất cứ điều gì vào đây. Bạn có một số lời giải thích ở đầu bài viết của mình, nhưng sẽ tốt hơn nếu mở rộng về điều đó và cắt giảm mã.
David Richerby

Đây không phải là một trang web mã hóa. Xin vui lòng không đăng câu trả lời chỉ mã. Thay vào đó, chúng tôi muốn bạn giải thích các ý tưởng đằng sau câu trả lời của bạn và cung cấp mã giả ngắn gọn cho thuật toán của bạn.
DW

-1

giải pháp của tôi:

    vc = train['classes'].value_counts()
    vc = dict(sorted(vc.items()))
    df = pd.DataFrame()
    train['col_for_sort'] = 0.0

    i=1
    for k,v in vc.items():
        step = train.shape[0]/v
        indent = train.shape[0]/(v+1)
        df2 = train[train['classes'] == k].reset_index(drop=True)
        for j in range(0, v):
        df2.at[j, 'col_for_sort'] = indent + j*step + 0.0001*i   
    df= pd.concat([df2,df])
    i+=1

    train = df.sort_values('col_for_sort', ascending=False).reset_index(drop=True)
    del train['col_for_sort']

Vui lòng sử dụng mã giả (với một số nhận xét cần thiết) để mô tả thuật toán của bạn.
xskxzr

Đây không phải là một trang web mã hóa. Xin vui lòng không đăng câu trả lời chỉ mã. Thay vào đó, chúng tôi muốn bạn giải thích các ý tưởng đằng sau câu trả lời của bạn và cung cấp mã giả ngắn gọn cho thuật toán của bạn.
DW
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.