JavaScript (ES6) 126 130 104 115 156 162 194
Setelah semua komentar dan test case dalam jawaban @ CarpetPython, kembali ke algoritma pertama saya. Sayangnya, solusi cerdas tidak berhasil. Implementasinya diperpendek sedikit, masih mencoba semua solusi yang mungkin, menghitung jarak kuadrat dan menjaga minimum.
Edit Untuk setiap elemen keluaran bobot w, 'semua' nilai yang mungkin hanya 2: trunc (w * s) dan trunc (w * s) +1, sehingga hanya ada (2 ** elemensts) solusi yang mungkin untuk dicoba.
Q=(s,w)=>
(n=>{
for(i=0;
r=q=s,(y=i++)<1<<w.length;
q|r>n||(n=r,o=t))
t=w.map(w=>(f=w*s,q-=d=0|f+(y&1),y/=2,f-=d,r+=f*f,d));
})()||o
Uji di Firefox / konsol FireBug
;[[ 1, [0.4, 0.3, 0.3] ]
, [ 3, [0, 1, 0] ]
, [ 4, [0.3, 0.4, 0.3] ]
, [ 5, [0.3, 0.4, 0.3] ]
, [ 21, [0.3, 0.2, 0.5] ]
, [ 5, [0.1, 0.2, 0.3, 0.4] ]
, [ 4, [0.11, 0.3, 0.59] ]
, [ 10, [0.47, 0.47, 0.06] ]
, [ 10, [0.43, 0.43, 0.14] ]
, [ 11, [0.43, 0.43, 0.14] ]]
.forEach(v=>console.log(v[0],v[1],Q(v[0],v[1])))
Keluaran
1 [0.4, 0.3, 0.3] [1, 0, 0]
3 [0, 1, 0] [0, 3, 0]
4 [0.3, 0.4, 0.3] [1, 2, 1]
5 [0.3, 0.4, 0.3] [1, 2, 2]
21 [0.3, 0.2, 0.5] [6, 4, 11]
5 [0.1, 0.2, 0.3, 0.4] [0, 1, 2, 2]
4 [0.11, 0.3, 0.59] [1, 1, 2]
10 [0.47, 0.47, 0.06] [5, 5, 0]
10 [0.43, 0.43, 0.14] [4, 4, 2]
11 [0.43, 0.43, 0.14] [5, 5, 1]
Itu solusi yang lebih cerdas. Lulus tunggal pada susunan bobot.
Untuk setiap pass saya menemukan nilai maks saat ini di w. Saya mengubah nilai ini di tempat dengan nilai integer tertimbang (dibulatkan), jadi jika s == 21 dan w = 0,4, kami mendapat 0,5 * 21 -> 10,5 -> 11. Saya menyimpan nilai ini dinegasikan, sehingga tidak bisa dapat ditemukan sebagai maks di loop berikutnya. Kemudian saya mengurangi jumlah total yang sesuai (s = s-11) dan juga mengurangi total bobot dalam variabel f.
Loop berakhir ketika tidak ada maks di atas 0 yang dapat ditemukan (semua nilai! = 0 telah dikelola).
Akhirnya saya mengembalikan nilai yang diubah menjadi positif lagi.
Peringatan kode ini mengubah array bobot di tempat, sehingga harus dipanggil dengan salinan array asli
F=(s,w)=>
(f=>{
for(;j=w.indexOf(z=Math.max(...w)),z>0;f-=z)
s+=w[j]=-Math.ceil(z*s/f);
})(1)||w.map(x=>0-x)
Percobaan pertama saya
Bukan solusi yang cerdas. Untuk setiap kemungkinan hasil, ini mengevaluasi perbedaan, dan menjaga minimum.
F=(s,w,t=w.map(_=>0),n=NaN)=>
(p=>{
for(;p<w.length;)
++t[p]>s?t[p++]=0
:t.map(b=>r+=b,r=p=0)&&r-s||
t.map((b,i)=>r+=(z=s*w[i]-b)*z)&&r>n||(n=r,o=[...t])
})(0)||o
Tidak Diikat Dan dijelaskan
F=(s, w) =>
{
var t=w.map(_ => 0), // 0 filled array, same size as w
n=NaN, // initial minumum NaN, as "NaN > value" is false for any value
p, r
// For loop enumerating from [1,0,0,...0] to [s,s,s...s]
for(p=0; p<w.length;)
{
++t[p]; // increment current cell
if (t[p] > s)
{
// overflow, restart at 0 and point to next cell
t[p] = 0;
++p;
}
else
{
// increment ok, current cell is the firts one
p = 0;
r = 0;
t.map(b => r += b) // evaluate the cells sum (must be s)
if (r==s)
{
// if sum of cells is s
// evaluate the total squared distance (always offset by s, that does not matter)
t.map((b,i) => r += (z=s*w[i]-b)*z)
if (!(r > n))
{
// if less than current mininum, keep this result
n=r
o=[...t] // copy of t goes in o
}
}
}
}
return o
}