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:
Hitung jarak antara setiap item dan item berikutnya dengan nilai yang sama.
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 adalah0
; - Untuk hasilnya
[1, 1, 2, 2, 3, 3]
, jaraknya adalah[1, 5, 1, 5, 1, 5]
, dan standar deviasinya adalah2
; - 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.
Jawaban:
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:
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:
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:
Selanjutnya, hapus B dari heap, output dan perbarui, lalu tambahkan kembali ke heap:
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:
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.Menjalankan program pengujian saya dengan contoh pertama OP, saya mendapatkan:
Jadi algoritma saya bekerja untuk masalah sepele dari semua yang dianggap sama.
Untuk masalah kedua yang diposting OP, saya dapat:
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:
Jadi dua tes saya adalah:
Dan hasil saya:
Dan untuk contoh kedua:
sumber
"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).
Tapi saya sarankan Anda mulai dengan anil simulasi. Itu hal pertama yang akan saya coba, karena saya pikir itu mungkin berhasil.
sumber
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.
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/ni−v|
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/nj−v|
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.
sumber
[0, 0, 0, 0, 1, 1, 1, 2, 2, 3]
dan v4
, kami akan menempatkan nilai pertama1
(10/3 = 3.33
, paling dekat dengan v), lalu2
(10/2 = 5
, paling dekat berikutnya), lalu0
(10/4 = 2.5
)? Atau: dapatkah Anda memberikan contoh "mengurangi penyimpangan rata-rata jarak dari nilai v"?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.
Hasil untuk
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
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]
.sumber
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.
sumber
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;
}
sumber
solusi saya:
sumber