Algoritma untuk mendistribusikan barang “secara merata”


25

Saya sedang mencari algoritma untuk mendistribusikan nilai dari daftar sehingga daftar yang dihasilkan sebagai "seimbang" atau "didistribusikan secara merata" sebanyak mungkin (dalam tanda kutip karena saya tidak yakin ini adalah cara terbaik untuk menggambarkannya ... nanti saya akan memberikan cara untuk mengukur apakah hasilnya lebih baik dari yang lain).

Jadi, untuk daftar:

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

Salah satu hasil terbaik, setelah mendistribusikan kembali nilai-nilai, adalah:

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

Mungkin ada hasil lain sebaik ini, dan tentu saja ini menjadi lebih rumit dengan seperangkat nilai yang kurang seragam.

Ini adalah cara mengukur apakah hasilnya lebih baik daripada yang lain:

  1. Hitung jarak antara setiap item dan item berikutnya dengan nilai yang sama.

  2. Hitung simpangan baku untuk set jarak itu. Dispersi yang lebih rendah berarti hasil yang lebih baik.

Pengamatan:

  • Ketika menghitung jarak dan akhir daftar tercapai tanpa menemukan item dengan nilai yang sama, kita kembali ke awal daftar. Jadi, paling banyak, item yang sama akan ditemukan dan jarak untuk item itu akan menjadi panjang daftar. Ini berarti bahwa daftarnya adalah siklik ;
  • Daftar tipikal memiliki ~ 50 item dengan ~ 15 nilai berbeda dalam jumlah bervariasi.

Begitu:

  • Untuk hasilnya [1, 2, 3, 1, 2, 3], jaraknya adalah [3, 3, 3, 3, 3, 3], dan standar deviasinya adalah 0;
  • Untuk hasilnya [1, 1, 2, 2, 3, 3], jaraknya adalah [1, 5, 1, 5, 1, 5], dan standar deviasinya adalah 2;
  • Yang membuat hasil pertama lebih baik daripada yang kedua (penyimpangan lebih rendah lebih baik).

Dengan definisi-definisi ini, saya meminta petunjuk algoritma atau strategi mana yang harus saya cari.


Sepertinya Anda ingin menyelesaikan masalah (varian optimasi) partisi , setidaknya secara perkiraan. Mungkin ada banyak algoritma untuk yang itu!
Raphael

Membaca ulang ini, mengapa menghitung kemunculan semua nilai dan kemudian menempatkan nilai secara siklis tidak selalu menghasilkan solusi optimal?
Raphael

Jawaban:


8

Saya menemukan pertanyaan ini sambil meneliti masalah serupa: penambahan cairan yang optimal untuk mengurangi stratifikasi. Sepertinya solusi saya akan berlaku untuk situasi Anda, juga.

Jika Anda ingin mencampur cairan A, B, dan C dalam proporsi 30,20,10 (yaitu, 30 unit A, 20 unit B, dan 10 unit C), Anda berakhir dengan stratifikasi jika Anda menambahkan semua A, lalu semua B, dan semua C. Anda lebih baik mencampur unit yang lebih kecil. Misalnya, lakukan penambahan unit tunggal dalam urutan [A, B, A, C, B, A]. Itu akan mencegah stratifikasi sama sekali.

Cara yang saya temukan untuk melakukannya adalah memperlakukannya sebagai semacam penggabungan, menggunakan antrian prioritas. Jika saya membuat struktur untuk menjelaskan penambahan:

MergeItem
    Item, Count, Frequency, Priority

Frekuensi dinyatakan sebagai "satu setiap N". Jadi A, yang ditambahkan tiga dari enam kali, memiliki frekuensi 2 (6/3).

Dan inisialisasi heap yang awalnya berisi:

(A, 3, 2, 2)
(B, 2, 3, 3)
(C, 1, 6, 6)

Sekarang, saya menghapus item pertama dari heap dan mengeluarkannya. Kemudian kurangi hitungannya dengan 1 dan tambah Prioritas dengan Frekuensi dan tambahkan kembali ke heap. Tumpukan yang dihasilkan adalah:

(B, 2, 3, 0)
(A, 2, 2, 4)
(C, 1, 6, 6)

Selanjutnya, hapus B dari heap, output dan perbarui, lalu tambahkan kembali ke heap:

(A, 2, 2, 4)
(C, 1, 6, 6)
(B, 1, 3, 6)

Jika saya melanjutkan dengan cara itu, saya mendapatkan campuran yang diinginkan. Saya menggunakan pembanding khusus untuk memastikan bahwa ketika item Prioritas yang sama dimasukkan ke dalam tumpukan, item dengan nilai Frekuensi tertinggi (yaitu yang paling jarang) dipesan terlebih dahulu.

Saya menulis deskripsi yang lebih lengkap tentang masalah dan solusinya di blog saya, dan menyajikan beberapa kode C # yang menggambarkannya. Lihat Mendistribusikan item secara merata dalam daftar .

Perbarui setelah komentar

Saya pikir masalah saya mirip dengan masalah OP, dan karena itu solusi saya berpotensi berguna. Saya minta maaf karena tidak membingkai jawaban saya lebih lanjut dalam hal pertanyaan OP.

Keberatan pertama, bahwa solusi saya menggunakan A, B, dan C daripada 0, 1, dan 2, mudah diatasi. Ini hanya masalah nomenklatur. Saya merasa lebih mudah dan kurang membingungkan untuk memikirkan dan mengatakan "dua A" daripada "dua 1". Tetapi untuk tujuan diskusi ini saya telah memodifikasi hasil saya di bawah ini untuk menggunakan nomenklatur OP.

Tentu saja masalah saya berkaitan dengan konsep jarak. Jika Anda ingin "menyebar semuanya secara merata," jarak tersirat. Tapi, sekali lagi, itu adalah kegagalan saya karena tidak cukup menunjukkan bagaimana masalah saya mirip dengan masalah OP.

Saya menjalankan beberapa tes dengan dua contoh yang diberikan OP. Itu adalah:

[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]

Dalam nomenklatur saya, masing-masing dinyatakan sebagai [2,2,2] dan [4,3,2,1]. Yaitu, dalam contoh terakhir, "4 item tipe 0, 3 item tipe 1, 2 item tipe 2, dan 1 item tipe 3."

Saya menjalankan program pengujian saya (seperti yang dijelaskan langsung di bawah), dan telah memposting hasil saya. Tanpa masukan dari OP, saya tidak bisa mengatakan apakah hasil saya mirip, lebih buruk daripada, atau lebih baik dari itu. Saya juga tidak dapat membandingkan hasil saya dengan hasil orang lain karena tidak ada orang lain yang memposting.

Saya dapat mengatakan, bagaimanapun, bahwa algoritma menyediakan solusi yang baik untuk masalah saya menghilangkan stratifikasi ketika mencampur cairan. Dan sepertinya itu memberikan solusi yang masuk akal untuk masalah OP.

Untuk hasil yang ditunjukkan di bawah ini, saya menggunakan algoritma yang saya perinci dalam entri blog saya, dengan prioritas awal yang ditetapkan Frequency/2, dan pembanding tumpukan diubah untuk mendukung item yang lebih sering. Kode yang dimodifikasi ditampilkan di sini, dengan garis yang dimodifikasi dikomentari.

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;
    }
}

Menjalankan program pengujian saya dengan contoh pertama OP, saya mendapatkan:

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

Jadi algoritma saya bekerja untuk masalah sepele dari semua yang dianggap sama.

Untuk masalah kedua yang diposting OP, saya dapat:

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

Saya tidak melihat cara yang jelas untuk memperbaiki itu. Bisa diatur ulang untuk membuat jarak untuk item 0 [2,3,2,3] atau pengaturan 2 dan 3 lainnya, tetapi itu akan mengubah penyimpangan untuk item 1 dan / atau 2. Saya benar-benar tidak tahu apa "optimal" ada dalam situasi ini. Apakah lebih baik untuk memiliki penyimpangan yang lebih besar pada item yang lebih sering atau lebih jarang?

Karena tidak memiliki masalah lain dari OP, saya menggunakan deskripsinya untuk mengatasinya sendiri. Dia mengatakan dalam posnya:

Daftar tipikal memiliki ~ 50 item dengan ~ 15 nilai berbeda dalam jumlah bervariasi.

Jadi dua tes saya adalah:

[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

Dan hasil saya:

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

Dan untuk contoh kedua:

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

@ WD Silakan lihat pembaruan saya. Saya percaya bahwa saya menunjukkan bagaimana masalah saya mirip dengan masalah OP, dan bagaimana algoritma saya memberikan solusi untuk masalah OP.
Jim Mischel

Barang bagus! Terima kasih atas pembaruan yang luar biasa. Terpilih.
DW

Cukup menarik, seperti yang saya katakan sebelumnya. Kesederhanaan ide itu menarik. Saya tidak punya waktu untuk membaca semuanya dengan seksama. Apakah solusi Anda benar-benar memperhitungkan siklus pertanyaan asli? Mungkin ada cara untuk mengadaptasinya untuk tujuan itu, tetapi saya tidak sepenuhnya yakin.
babou

@Babou: Perhitungan jarak saya membungkus, seperti yang Anda lihat dalam hasil, tetapi algoritma itu sendiri tidak membuat kelonggaran khusus untuk sifat siklus masalah OP. Saya juga tidak melihat cara apa pun agar saya dapat menyesuaikan algoritme untuk melakukannya. Atau, dalam hal ini, bagaimana memperhitungkan sifat siklus akan meningkatkan hasilnya. Meskipun menarik untuk mempertimbangkan menggandakan semua jumlah (yaitu mengubah [3,2,1] menjadi [6,4,2]), yang secara efektif merupakan hal yang sama. Kecurigaan saya adalah bahwa algoritma akan menghasilkan hasil yang identik.
Jim Mischel

6

"Bau" ini seperti NP-hard. Jadi, apa yang Anda lakukan ketika Anda memiliki masalah NP-hard? Lempar heuristik, atau algoritma perkiraan, atau gunakan pemecah SAT.

Dalam kasus Anda, jika Anda tidak memerlukan solusi optimal mutlak, satu titik awal yang masuk akal mungkin adalah mencoba anil simulasi . Ada cara alami untuk mengambil solusi kandidat dan memindahkannya ke solusi kandidat terdekat: secara acak pilih dua item dalam daftar, dan tukar. Simulasi anil akan secara iteratif mencoba untuk meningkatkan solusi. Anda dapat menemukan banyak sumber daya pada anil simulasi, jika Anda tidak terbiasa dengannya. Anda juga dapat bereksperimen dengan set "gerakan lokal" lainnya yang membuat perubahan kecil pada solusi kandidat, dengan harapan untuk meningkatkannya secara bertahap (yaitu, mengurangi deviasi standar jarak).

ttt2xi,jxi,jijt2

Tapi saya sarankan Anda mulai dengan anil simulasi. Itu hal pertama yang akan saya coba, karena saya pikir itu mungkin berhasil.


Apakah saran Anda adalah cara standar untuk mengatasi masalah penjadwalan seperti ini. Saya kira ada beberapa perangkat lunak komersial untuk ini. Bagaimana mereka menanganinya?
babou

@ Babou, pertanyaan bagus - Saya tidak tahu!
DW

Saya lebih lanjut mengembangkan rincian algoritma saya, tetapi saya ragu aplikasi yang sudah ada akan menggunakannya. Sebenarnya, saya bahkan bertanya-tanya apakah aplikasi penjadwalan berurusan dengan masalah semacam ini. Saya telah meminta info tentang SE.softwarerecs, karena saya tidak melihat cara mengajukan pertanyaan di sini, selain sebagai komentar seperti yang baru saja saya lakukan.
babou

The optimum solusi mungkin NP-keras. Tetapi solusi yang cukup bisa diterapkan adalah O (n log k), di mana n adalah jumlah total item dan k adalah jumlah jenis item. Lihat jawaban saya, dan posting blog saya yang ditautkan.
Jim Mischel

2

Sketsa algoritma heuristik

Saya tidak punya solusi tepat untuk masalah ini. Tetapi karena komentar Raphael menunjukkan sepertinya masalah partisi, yang algoritma heuristiknya telah dikembangkan, saya akan mencoba pendekatan heuristik. Ini hanya sketsa dari algoritma heuristik.

vn[1..n]ini

nvnvn/nv

v

in/ninmodnin/ni

Itu akan memandu algoritma kami.

Tapi pertama, kami mencatat bahwa nilai-nilai tunggal (terjadi hanya sekali) akan selalu memiliki terkait jarak yang sama . Karenanya penempatan mereka tidak masalah dan dapat diabaikan oleh algoritma. Mereka hanya akan mengambil slot apa pun yang tersisa tersedia di akhir.n

Kemudian, karena jarak-jarak yang menyimpang paling banyak harus menjadi yang paling tepat untuk berkontribusi lebih sedikit pada jumlah kuadrat, kami mencoba untuk menempatkan pertama nilai-nilai yang paling menyimpang, yaitu nilai sedemikian rupa sehingga adalah yang terbesar.| n / n i - v |i|n/niv|

Ini mungkin nilai dengan sangat sedikit dari sangat sedikit kejadian pada awalnya. Saya pikir itu tidak benar-benar membuat perbedaan, karena kendala yang dibuat oleh menempati slot adalah proporsi dari jumlah nilai yang ditempatkan dengan baik (?).

Nilai pertama yang dipertimbangkan dapat ditempatkan tanpa kendala. Kemudian Nilai-nilai lain harus ditempatkan untuk meminimalkan kontribusinya terhadap standar deviasi, tetapi hanya dalam slot yang dibiarkan bebas oleh nilai apa pun yang telah ditempatkan sebelumnya.

Penempatan kemunculan nilai dalam slot yang tersisa dapat dilakukan dengan algoritma pemrograman dinamis, sehingga untuk menggabungkan perhitungan yang menempatkan jumlah nilai yang sama antara dua posisi, menjaga hanya mereka yang memiliki kontribusi minimal terhadap standar deviasi (yaitu nilai minimum untuk jumlah kuadrat dari penyimpangan mereka).

Kadang-kadang, akan ada beberapa solusi minimal. Dalam hal ini Anda mencoba untuk melestarikan beberapa kelonggaran dengan memilih solusi minimal yang memiliki slot remaing paling merata. Ini dapat dihitung, untuk setiap solusi, dengan menghitung standar deviasi jarak antara slot gratis yang tersisa (dengan repect dengan nilai rata-rata, bukan sehubungan dengan ).v

Kemudian Anda ulangi untuk nilai sisa sehinggaadalah yang terbaik, seterusnya sampai semua nilai yang tidak tunggal ditempatkan.| n / n j - v |j|n/njv|

Lalu Anda menempatkan nilai singleton di slot yang tersisa.

Saya percaya ini umumnya harus memberikan solusi yang masuk akal, tetapi saya belum tahu bagaimana membuktikannya atau memperkirakan kesenjangan dengan solusi yang optimal.


Saya memiliki kesan yang sama bahwa tidak masalah jika kita mulai dengan yang paling umum atau paling tidak umum, mengesampingkan para lajang. Strategi yang tampaknya memberi saya hasil terbaik mulai mengurutkan nilai berdasarkan kejadian, dan menempatkannya secara berurutan mulai dari yang paling banyak terjadi. Ini secara alami meninggalkan lajang sampai akhir.
moraes

@moraes Yang penting adalah memesannya dengan mengurangi penyimpangan rata-rata jarak dari nilai . Ini biasanya akan bergantian paling sedikit dan paling umum, sehingga mulai dari kedua ujung ke tengah (jumlah kejadian dekat dengan , karena adalah jarak rata-rata). Kecuali lajang, tentu saja. n / v V.vn/vV
babou

Apakah maksud Anda, untuk daftar dengan 10 nilai [0, 0, 0, 0, 1, 1, 1, 2, 2, 3]dan v 4, kami akan menempatkan nilai pertama 1( 10/3 = 3.33, paling dekat dengan v), lalu 2( 10/2 = 5, paling dekat berikutnya), lalu 0( 10/4 = 2.5)? Atau: dapatkah Anda memberikan contoh "mengurangi penyimpangan rata-rata jarak dari nilai v"?
moraes

1
Tidak, saya melakukan yang sebaliknya. Mengambil contoh Anda, urutan penentuan posisi adalah O pertama karena jarak rata-rata 2,5 menyimpang paling banyak dari v = 4, lalu 2, lalu 1, dan singleton 3. - - - Apakah ypu menyarankan agar saya harus menulis ulang dengan lebih jelas beberapa bagian dari penjelasan saya untuk strategi ini?
babou

Tidak apa-apa. Saya akan mencoba sesuatu di sepanjang ide ini dan melaporkan kembali.
moraes

1

Sepertinya saya sangat terlambat ke pesta, tetapi memposting kalau-kalau ada yang mengalami ini lagi. Solusi saya mirip dengan @ babou's plus. Sebelumnya hari ini, saya memiliki masalah penjadwalan dalam sistem tertanam yang membawa saya ke utas ini. Saya memiliki implementasi khusus untuk masalah saya di C, tapi saya pikir saya akan memposting solusi yang lebih umum dalam Python di sini (versi C rumit oleh fakta bahwa saya telah membatasi diri saya pada tumpukan kecil, ukuran tetap dan tidak ada memori alokasi, jadi saya melakukan seluruh algoritma di tempat). Teknik anti-aliasing yang digunakan di bawah ini adalah sesuatu yang mungkin Anda gunakan untuk menggambar garis pada layar dengan warna 2 bit. Algoritme di sini mencapai skor yang lebih rendah (yaitu, lebih baik) ketika diukur menggunakan jumlah deviasi standar untuk input yang digunakan oleh Jim Mischel daripada solusi tertentu.

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

Hasil untuk

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)

Jika diberikan input dari formulir yang ditentukan oleh @moraes, seseorang dapat mengonversinya menjadi bentuk yang dapat digunakan oleh fungsi ini dalam langkah-langkah O (n) menggunakan bit memori Big Omega (n * log (n)) di mana n adalah jumlah item ( dalam daftar dengan 255 elemen, Anda tidak akan memerlukan lebih dari 255 byte tambahan) dengan mempertahankan array paralel dengan jumlah pengulangan. Sebagai alternatif, seseorang dapat melakukan sepasang jenis in-place dengan O (1) memori tambahan.

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)

Sunting: Saya tahu solusi ini tidak menghasilkan output optimal oleh counterexample. Input dari [6, 2, 1]menghasilkan [0, 1, 0, 0, 2, 0, 0, 1, 0]; solusi yang lebih baik adalah [0, 0, 1, 0, 2, 0, 0, 1, 0].


Saya percaya saya menjelaskan algoritma saya di komentar kode dan dasar untuk algoritma di pembukaan.
lungj

Saya lebih suka melihat deskripsi mandiri dari ide-ide di balik algoritma Anda dan pseudocode ringkas untuk algoritma. Saat ini yang saya lihat dalam teks pengantar adalah (1) pendekatan Anda mirip dengan @ babou dan (2) ia menggunakan teknik anti-aliasing (entah bagaimana). Juga, tidak semua orang di sini membaca Python. Bagaimanapun, ini adalah jawaban lama, jadi saya mengerti jika Anda tidak ingin memperbaikinya, tapi saya hanya mencatat harapan kami di situs ini - tidak hanya untuk Anda, tetapi untuk orang lain yang mungkin menemukan halaman ini di masa depan dan cenderung menjawab.
DW

0

Algoritma ini bekerja dengan array bilangan bulat, di mana setiap bilangan bulat mewakili kategori yang berbeda. Itu menciptakan array terpisah untuk setiap kategori. Misalnya, jika array awal adalah [1, 1, 1, 2, 2, 3], itu akan membuat tiga array, [3], [2, 2], [1, 1, 1].

Dari sana ia secara rekursif menggabungkan dua array terkecil (dalam contoh ini, [3], dan [2,2]) dan menempatkan penempatan elemen-elemen dari array yang lebih kecil ke dalam array terkecil kedua yang sebagian besar didasarkan pada rasio angka kemunculan dari kategori yang lebih besar vs yang lebih kecil. Dalam contoh ini, kita akan berakhir dengan [2,3,2]. Maka akan menggunakan array ini sebagai array yang lebih kecil yang akan digabungkan ke dalam array yang lebih besar berikutnya, sampai hanya ada satu array yang tersisa.

<?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
Ini bukan situs pengkodean. Tolong jangan posting jawaban hanya kode. Sebagai gantinya, kami ingin Anda menjelaskan ide-ide di balik jawaban Anda, dan memberikan pseudocode ringkas untuk algoritma Anda.
DW

Selamat Datang di Ilmu Komputer ! Kalau-kalau Anda tidak sadar atau Anda lupa sejenak, membaca kode dalam satu bahasa tertentu biasanya merupakan salah satu tugas terberat yang bisa kita miliki, kadang-kadang bahkan jika kode itu ditulis sendiri. Itu adalah bagian dari alasan mengapa kami sangat tidak menghargai kode nyata di situs ini, meskipun itu mungkin merepresentasikan lebih banyak pekerjaan daripada pseudocode yang ditulis dengan longgar. Tentu saja, saya menghargai semua kode kerja aktual yang dapat dijalankan atau berbinar segera.
Apass.Jack

Penjelasannya ada di sana. dalam kode demonstrasi yang dikomentari; yang tidak dalam beberapa sintaksis purba seperti APL, tetapi sintaksis yang mudah dipahami cukup dekat dengan kode semu. Apakah akan membantu jika penjelasan saya tidak menggunakan font monospace?
vtim

Iya nih. Itu membantu. Tidak semua orang membaca PHP, mungkin tidak semua orang dapat menentukan apa yang dikomentari (mungkin itu argumen manusia jerami) atau hanya tidak ingin membaca blok kode, dan menafsirkannya, tetapi membaca idenya, yang telah Anda sertakan di bagian atas dan itu menceritakan segalanya. +1 dari saya. Kode Anda bersih dan didokumentasikan dengan baik, tetapi kami bukan situs pembuat kode, jadi deskripsi tekstual penting di sini. Terima kasih atas hasil edit Anda.
Evil

-1

KODE ANSI C

Kode ini bekerja dengan membayangkan garis lurus dalam ruang n dimensi (di mana n adalah jumlah kategori) melewati titik asal dengan vektor arah (v1, v2, ..., vi, ... vn) di mana vi adalah jumlah item dalam kategori i. Mulai dari asal tujuannya adalah untuk menemukan titik terdekat berikutnya ke garis. Dengan menggunakan contoh [0 0 0 0 0 1 1 1 2 2 2 3] hasilnya adalah [0 1 2 0 3 1 0 2 0 1 2 0]. Menggunakan contoh Lungj [0 0 0 0 0 0 1 1 2] kita dapatkan [0 1 0 0 2 0 0 1 0], yang persis sama dengan hasil Lungj.

Algoritma dibuat lebih efisien dengan hanya menggunakan bilangan bulat aritmatika dan hanya mempertimbangkan delta antara jarak dari setiap titik ke garis.

#define MAXCATEGORIES 100

int main () {int i = 0; int j = 0; int catsize = 0; int vector [MAXCATEGORIES]; int point [MAXCATEGORIES]; kategori int = 0; int totalitems = 0; int terbaik = 0; panjang d2 = 0L; vp panjang = 0L; long v2 = 0L; delta panjang = 0L; beta panjang = 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
Selamat datang di situs ini! Dari segi format, Anda perlu memberi indentasi pada setiap baris kode Anda dengan empat spasi agar sistem mendapatkan mark-up yang benar. Secara umum, kami tidak mencari blok kode yang besar sebagai jawaban atas pertanyaan dan, khususnya, rutinitas entri data Anda tidak menambahkan apa pun di sini. Anda memiliki beberapa penjelasan di bagian atas posting Anda, tetapi akan lebih baik untuk memperluasnya dan mengurangi kode.
David Richerby

Ini bukan situs pengkodean. Tolong jangan posting jawaban hanya kode. Sebagai gantinya, kami ingin Anda menjelaskan ide-ide di balik jawaban Anda, dan memberikan pseudocode ringkas untuk algoritma Anda.
DW

-1

solusi saya:

    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']

Silakan gunakan pseudocode (dengan beberapa komentar yang diperlukan) untuk menjelaskan algoritma Anda.
xskxzr

Ini bukan situs pengkodean. Tolong jangan posting jawaban hanya kode. Sebagai gantinya, kami ingin Anda menjelaskan ide-ide di balik jawaban Anda, dan memberikan pseudocode ringkas untuk algoritma Anda.
DW
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.