Можно решить рекурсией.
На каждом шаге получаем очередное слагаемое, округляя результат к ближайшему в «сетке» с учётом точности. Для целых это выглядит так:
Math.round(20 / 3) === 7 // 7 занесли в массив результата
20 - 7 = 13
// далее повторяем с 13 и делим уже на 3 - 1 === 2
Math.round(13 / 2) === 7 // 7 идёт в результат
13 - 7 === 6
// далее повторяем с 6 и делить надо бы на 2 - 1 === 1
// т.е. осталось одно число в результат. Просто вписываем туда остаток 6
С точностью надо умножать/делить на эту точность перед тем, как округлять к ближайшему целому. Но тут вылезают проблемы с точностью плавающей точки. Поэтому надо уточнить, какие могут быть значения точности: до опр. знака после запятой, или вообще любое значение типа
0.137456
?
function split(n, divisor, q, result) {
q = q || 1;
result = result || [];
const m = q * Math.round(n / divisor / q);
result.push(m);
n -= m;
if( --divisor > 1) return split(n, divisor, q, result);
else {
result.push(n);
return result.sort((a,b) => a - b); // сортировка по возрастанию, на всякий случай
}
}
split(20, 3) // [6, 7, 7]
split(20, 3, 0.1) // [6.6000000000000005, 6.7, 6.7]
split(27.8, 4, 0.1) // [6.9, 6.9, 7, 7]