I wrote this JavaScript code following Zedka29's answer, but I ran into a few bugs when selecting different amounts of numbers to generate. In your example, if 20 was the max of your range, you would end up having a gap left over as it is likely the first number will be less than 20 in that case.
function genRand(min, max, target, numItems) {
let total = 0;
let maxNum = Math.floor(target/min);
let minNum = Math.ceil(target/max);
if (!numItems) numItems = Math.floor(Math.random() * (maxNum - minNum) + minNum);
let avg = Math.round(target / numItems);
let tries = 0;
while (total < target && tries < maxNum) {
let tmp = Math.floor(Math.random() * (max - min) + min);
if (total + tmp > target) {
tmp = target - total;
}
else if (total + tmp > target - min) {
tmp += target - (total + tmp);
if (tmp > max) {
// SOMETHING WRONG
}
}
else {
if (tmp < avg) {
min += (avg - tmp);
}
else if (tmp > avg) {
max -= (tmp - avg);
}
}
total += tmp;
console.log(tmp + ', new min: ' + min + ', new max: ' + max);
console.log('total: ' + total);
++tries;
}
console.log('numItems ' + numItems);
console.log('nums generated: ' + tries);
}
genRand(10,20,100, 5);
However, after running it in this post a few times, I am realizing the last number seems to always be 30. 10, the minimum, more than the maximum... I wonder if that info would be useful.
Edit 1
After struggling to figure out what went wrong with Zedka29's answer, I developed a rather brute-force solution to this problem using recursion and arrays. The code below will generate all possible unique sorted combinations of numbers within your range that add up to the total. To actually generate the random numbers you need, sort your current array of numbers and check that it is in the array(s) generated by the brute-force method described here. Note: this method uses more memory than is necessary and could still be optimized, but will protect against having a remaining number too large or too little regardless of what the user input.
Here is the link to the repl.it: https://repl.it/@geraldgehrke/RandomLengthsTesting-1
(function(){
const total = 41;
const size = 5;
let arr = [];
let cntrs = new Array(size);
const min = 5;
const max = 10;
let maxNum = Math.floor(total/min);
let minNum = Math.ceil(total/max);
if (minNum > maxNum) {
minNum = maxNum;
}
for (let i = minNum; i <= maxNum; ++i) {
cntrs = new Array(i);
arr = [];
generateSums(total, i, arr, cntrs, min, max);
}
function generateSums(total, size, arr, cntrs, min, max) {
function count(index) {
++cntrs[index];
let sum = 0;
for (let i of cntrs) {
sum += i;
}
if (sum == total) {
arr.push(cntrs.slice(0));
}
if (index < size - 1) {
count(index + 1);
}
}
function loop(prevIndex, loopLevel, maxLoopLevel) {
if (loopLevel < maxLoopLevel) {
for (let i = prevIndex; i < size; ++i) {
loop(i, loopLevel + 1, maxLoopLevel);
}
}
else {
for (let i = prevIndex; i < size; ++i) {
cntrs.fill(min, i+1);
count(i);
}
}
}
cntrs.fill(min, 0);
let sum = 0;
for (let i of cntrs) {
sum += i;
}
if (sum == total) {
arr.push(cntrs.slice(0));
}
count(0);
for (let i = 1; i < max-min; ++i) {
loop(0,1,i);
}
console.log(arr);
}
})();