Temukan set bobot yang optimal untuk ditambahkan ke set bobot tertentu

8

Dalam tantangan ini, Anda akan menerima daftar bobot yang dipisahkan koma sebagai input, seperti

1,3,4,7,8,11

Dan Anda harus menampilkan jumlah bobot terkecil yang dapat ditambahkan ke set itu. Misalnya, output untuk set ini adalah

1,3,7

Karena Anda bisa mewakili semua bobot itu hanya dengan ketiganya:

1     = 1
3     = 3
1+3   = 4
7     = 7
1+7   = 8
1+3+7 = 11

Mungkin ada lebih dari satu solusi. Misalnya, solusi Anda untuk input 1,2bisa 1,1atau 1,2. Selama menemukan jumlah bobot minimum yang dapat mewakili set input, ini adalah solusi yang valid.

Bobot tidak boleh digunakan lebih dari sekali. Jika Anda perlu menggunakannya dua kali, Anda harus mengeluarkannya dua kali. Misalnya, 2,3bukan solusi yang valid untuk 2,3,5,7karena Anda tidak dapat menggunakan 2dua kali untuk 2+2+3=7.

Input dijamin tidak memiliki nomor duplikat.

Ini adalah sehingga kode terpendek menurut jumlah karakter menang.

Akses jaringan dilarang (jadi tidak ada wgetsolusi "pintar" Anda @JohannesKuhn batuk batuk );)

Kasus paling sederhana:

1,5,6,9,10,14,15               => 1,5,9
7,14,15,21,22,29               => 7,14,15
4,5,6,7,9,10,11,12,13,15,16,18 => 4,5,6,7
2,3,5,7                        => 2,2,3 or 2,3,7

Dan beberapa yang lebih rumit:

10,16,19,23,26,27,30,37,41,43,44,46,50,53,57,60,61,64,68,71,77,80,84,87
  => 3,7,16,27,34
20,30,36,50,56,63,66,73,79,86
  => 7,13,23,43
27,35,44,46,51,53,55,60,63,64,68,69,72,77,79,81,86,88,90,95,97,105,106,114,123,132
  => 9,18,26,37,42
Gagang pintu
sumber
sangat mirip dengan codegolf.stackexchange.com/questions/12399/...
John Dvorak
@ Jan, satu perbedaan signifikan adalah bahwa tantangan yang Anda sebutkan membutuhkan set, sedangkan yang satu ini memungkinkan duplikat (misalnya, di 7,7,7,8atas), yang meningkatkan kerumitan banyak kali.
Cary Swoveland
Bisakah kita menganggap bobot input unik (jadi kita tidak harus menghapus dups, sesederhana itu)? Anda juga dapat mempertimbangkan untuk meminta solusi agar dapat menyelesaikan kasus uji yang diberikan; jika tidak, solusi terpendek dapat menjadi enumerator kasar yang hanya dapat menangani masalah kecil (misalnya, jika ada nbobot masukan dan mmerupakan yang terbesar, sebutkan semua urutan (1..m)dan untuk setiap urutan , sebutkan setiap kombinasi antara 1 dan ncontoh masing-masing elemen urutan.)
Cary Swoveland
@CarySwoveland Diedit untuk bagian "unik". Saya sudah memiliki test case.
Gagang Pintu
Bagaimana {7,7,7,8} bisa menjadi solusi? 8 tidak ada dalam set input.
DavidC

Jawaban:

3

Mathematica 80 75

Pembaruan: Lihat di bagian bawah pembaruan tentang tes terakhir Doorknob yang menantang, ditambahkan pada 5 November


Ini melewati semua kecuali tes terakhir. Namun, ia tidak berusaha menggunakan angka lebih dari sekali. Dan itu hanya mencari dari solusi yang merupakan himpunan bagian dari kumpulan data yang lebih besar.

Fungsi menghasilkan semua himpunan bagian dari himpunan data input dan kemudian menguji himpunan bagian yang dapat digunakan untuk membangun himpunan lengkap. Setelah himpunan bagian yang layak ditemukan, ia memilih set terkecil.

s=Subsets
f@i_:=GatherBy[Select[s@i,Complement[i, Total /@ s@#]=={}&],Length]〚1〛

Tes

f[{1, 3, 4, 7, 8, 11}]

{{1, 3, 7}}


f[{1, 5, 6, 9, 10, 14, 15}]

{{1, 5, 9}}


f[{7, 14, 15, 21, 22, 29}]

{{7, 14, 15}}


f[{4, 5, 6, 7, 9, 10, 11, 12, 13, 15, 16, 18}]

{{4, 5, 6, 7}}


f[{2, 3, 5, 7}]

{{2, 3, 5}, {2, 3, 7}}


Memperbarui

Di bawah ini saya akan memberikan analisis awal yang dapat membantu memulai menuju solusi.

Data:

data = {10, 16, 19, 23, 26, 27, 30, 37, 41, 43, 44, 46, 50, 53, 57, 60, 61, 64, 68, 71, 77, 80, 84, 87};

Berbeda dari pendekatan sebelumnya, kami ingin mempertimbangkan, dalam set solusi, angka-angka yang TIDAK muncul dalam set data.

Pendekatan ini memanfaatkan perbedaan mutlak antara pasangan angka dalam kumpulan data.

g[d_] := DeleteCases[Reverse@SortBy[Tally[Union[Sort /@ Tuples[d, {2}]] /. {a_, b_} :> Abs[a - b]], Last], {0, _}] 

Mari kita lihat berapa kali setiap perbedaan muncul; kami hanya akan mengambil 8 kasus pertama, mulai dari perbedaan paling umum].

g[data][[1;;8]]

{{7, 14}, {27, 13}, {34, 12}, {3, 11}, {20, 10}, {16, 10}, {4, 10}, {11, 9}}

14 pasang berbeda dengan 7; 13 pasang berbeda 27, dan seterusnya.

Sekarang mari kita uji himpunan bagian dimulai dengan {difference1}, {difference1, difference2}, dan seterusnya, sampai kita semoga dapat menjelaskan semua elemen asli dalam kumpulan data.

h mengungkapkan angka-angka dari himpunan asli yang tidak dapat dibangun dengan menyusun jumlah dari himpunan bagian.

h[t_] := Complement[data, Total /@ Subsets@t]

Pada percobaan kelima, masih ada 10 elemen yang tidak dapat dibentuk dari {7, 27, 34, 3, 20}:

h[{7, 27, 34, 3, 20}]

{16, 19, 26, 43, 46, 53, 60, 77, 80, 87}

Tetapi pada percobaan berikutnya, semua angka dari set data dicatat:

h[{7, 27, 34, 3, 20, 16}]

{}

Ini masih tidak ekonomis seperti {3,7,16,27,34}, tapi sudah dekat.


Masih ada beberapa hal tambahan yang perlu diperhatikan.

  1. Jika 1 ada di set data, itu akan diperlukan dalam set solusi.
  2. Mungkin ada beberapa "penyendiri" di set asli yang tidak dapat disusun dari perbedaan yang paling umum. Ini perlu dimasukkan terpisah dari tes perbedaan.

Ini lebih banyak masalah daripada yang bisa saya tangani saat ini. Tapi saya harap ini memberikan sedikit cahaya pada tantangan yang sangat menarik ini.

DavidC
sumber
hmm ... saat ini sedang menyusun testcase yang membutuhkan duplikat: P
Doorknob
Saya akan membiarkan solusi saya diposting untuk saat ini dan melihat apakah saya dapat menambahkan kondisi untuk menguji duplikat.
DavidC
3
Jika ada solusi di mana berat wdiulang, maka solusi yang sama dengan salah satu dari ws berubah 2 * wjuga berfungsi, karena Anda dapat menggunakan di 2 * wmana - mana Anda gunakan w + wsebelumnya. Ini dapat diulang sampai solusi tidak memiliki pengulangan. Oleh karena itu, Anda tidak perlu mencoba menggunakan pengulangan.
cardboard_box
Anda tidak benar-benar membutuhkan tanda kurung. Dapatkan s=Subsets;keluar dari fungsi
Dr. Belisarius
Benar tentang tanda kurung.
DavidC
0

Ruby 289

Ini adalah penghitungan langsung, sehingga akan mendapatkan solusi minimal, tetapi mungkin butuh bertahun-tahun - mungkin tahun cahaya - untuk menyelesaikan beberapa masalah. Semua "kasus paling sederhana" menyelesaikan paling banyak beberapa detik (meskipun saya mendapat masing-masing 7,8,14 dan 1,2,4 untuk kasus ke-3 dan ke-5). Tricky # 2 diselesaikan dalam waktu sekitar 3 jam, tetapi dua lainnya terlalu besar untuk dihitung, paling tidak untuk cara saya melakukannya.

Array ukuran nyang menghasilkan array yang diberikan dengan menjumlahkan himpunan bagian dari elemen-elemennya adalah ukuran minimal jika dapat ditunjukkan bahwa tidak ada array ukuran < nyang melakukan itu. Saya tidak bisa melihat cara lain untuk membuktikan optimalitas, jadi saya memulai penghitungan dengan himpunan bagian ukuran m, di mana mbatas bawah diketahui, dan kemudian menambah ukuran m+1setelah menghitung himpunan bagian ukuran mdan menunjukkan mereka tidak ada "rentang" yang diberikan array, dan sebagainya, sampai saya menemukan yang optimal. Tentu saja, jika saya telah menghitung semua himpunan bagian hingga ukuran n, saya bisa menggunakan heuristik untuk ukuran n +1, sehingga jika saya menemukan array spanning ukuran itu, saya akan tahu itu optimal. Adakah yang bisa menyarankan cara alternatif untuk membuktikan solusi yang optimal dalam kasus umum?

Saya telah menyertakan beberapa pemeriksaan opsional untuk menghilangkan beberapa kombinasi sejak awal. Menghapus cek itu akan menghemat 87 karakter. Mereka adalah sebagai berikut ( aadalah array yang diberikan):

  • array ukuran ndapat menghasilkan paling 2^n-1banyak angka positif yang berbeda; karenanya,, 2^n-1 >= a.sizeatau n >= log2(a.size).ceil("batas bawah" yang saya sebutkan di atas).
  • kandidat yang menghasilkan berbagai bukuran ndapat dikesampingkan jika:
    • b.min > a.min
    • sum of elements of b < a.max atau
    • b.max < v, dimana v = a.max.to_f/n+(n-1).to_f/2.ceil( to_fsedang konversi ke float).

Yang terakhir ini, yang diperiksa terlebih dahulu, mengimplementasikan

sum of elements of b <= sum(b.max-n+1..b.max) < a.max

Catatan vkonstan untuk semua array ukuran generator n.

Saya juga menggunakan pengamatan sangat kuat @ cardboard_box bahwa tidak perlu mempertimbangkan duplikat dalam array menghasilkan.

Dalam kode saya,

(1..a.max).to_a.combination(n) 

menghasilkan semua kombinasi angka 1 a.max, diambil npada satu waktu (di mana a.max = a.last = a[-1]). Untuk setiap kombinasi b:

(1...2**n).each{|j|h[b.zip(j.to_s(2).rjust(n,?0).split('')).reduce(0){|t,(u,v)|t+(v==?1?u:0)}]=0}

mengisi hash hdengan semua angka yang jumlah dari himpunan bagian tidak kosong dari b. Kunci hash adalah angka-angka itu; nilai-nilai itu berubah-ubah. (Saya memilih untuk mengatur yang terakhir ke nol.)

a.all?{|e|h[e]}}

memeriksa apakah setiap elemen dari array yang diberikan aadalah kunci dalam hash ( h[e] != nil, atau just h[e]).

Seharusnya

n = 3 and b=[2,5,7].

Lalu kami beralih pada rentang:

(1...2**8) = (1...8) # 1,2,..,7

Representasi biner dari setiap angka dalam kisaran ini digunakan untuk menusuk elemen-elemen dari bpenjumlahan. Untuk j = 3( jmenjadi indeks rentang),

3.to_s(2)                  # =>  "11"
"11".rjust(3,?0)           # => "011"
"011".split('')            # => ["0","1","1"]
[2,5,7].zip(["1","0","1"]) # => [[2,"0"],[5,"1"],[7,"1"]]
[[2,"0"],[5,"1"],[7,"1"]].reduce(0){|t,(u,v)|t+(v==?1?u:0)}]=0 # => t = 0+5+7 = 12 

Kode:

x=a[-1]
n=Math.log2(a.size).ceil
loop do
v=(1.0*x/n+(n-1)/2.0).ceil
(1..x).to_a.combination(n).each{|b|
next if b[-1]<v||b[0]>a[0]||b.reduce(&:+)<x
h={}
(1...2**n).each{|j|h[b.zip(j.to_s(2).rjust(n,?0).split('')).reduce(0){|t,(u,v)|t+(v==?1?u:0)}]=0}
(p b;exit)if a.all?{|e|h[e]}}
n+=1
end
Cary Swoveland
sumber