I'm looking to generate 6 random percentages that add up to 100 in JavaScript. I don't want the difference between the largest and smallest percentage to be more than say 20%.
What is the most efficient way of doing this?
Many thanks.
I'm looking to generate 6 random percentages that add up to 100 in JavaScript. I don't want the difference between the largest and smallest percentage to be more than say 20%.
What is the most efficient way of doing this?
Many thanks.
Share Improve this question asked Sep 6, 2013 at 16:12 user2743326user2743326 1231 silver badge6 bronze badges 2- Integers only? Is zero allowed as a value? Do they have to be unique or can there duplicates? – Niet the Dark Absol Commented Sep 6, 2013 at 16:13
- Preferably integers only. Zero would not be allowed as a value. Duplicates are allowed. Sorry I wasn't clearer. – user2743326 Commented Sep 6, 2013 at 16:36
4 Answers
Reset to default 3I had a similar problem. One nice thing about this is that not only are the numbers random, but which item the array is the largest is also random because the array has been shuffled.
Otherwise, most of the time the 1st number will be the largest, and the 2nd number will be the 2nd largest etc.
Another nice thing is that this will generate as many segments as you want, and you can have a cap for what the largest segment is. e.g. if you're trying to add up to 100, you won't end up with your first number being 99.9.
var generateProportion = function() {
var max = 100,
segmentMax = 60,
tempResults = [],
remaining = max,
segments = 5,
finalResults = [];
//create a series of random numbers and push them into an array
for (var i = 1; i <= segments; i++) {
var r = Math.random() * segmentMax;
if (i === segments) {
// the final segment is just what's left after the other randoms are added up
r = remaining;
}
tempResults.push(r);
// subtract them from the total
remaining -= r;
// no segment can be larger than what's remaining
segmentMax = remaining;
}
//randomly shuffle the array into a new array
while (tempResults.length > 0) {
var index = Math.floor(Math.random() * tempResults.length);
finalResults = finalResults.concat(tempResults.splice(index, 1));
}
return finalResults;
}
var proportion = generateProportion();
var total = proportion.reduce( (a,b) => a+b);
console.log(proportion, "=", total);
<script src="https://cdnjs.cloudflare./ajax/libs/d3/3.4.11/d3.min.js"></script>
I had to solve a similar problem. This is how I did it:
const random = (min, max) => {
return min + Math.random() * (max - min);
};
const randomFill = (amount, min, max) => {
const arr = [];
let total = 0;
// fill an array with random numbers
for (let i = 0; i < amount; i++) arr.push(random(min, max));
// add up all the numbers
for (let i = 0; i < amount; i++) total += arr[i];
// normalise so numbers add up to 1
for (let i = 0; i < amount; i++) arr[i] /= total;
return arr;
};
min
and max
can be anything. The actual values don't matter much because the total is normalised.
const values = randomFill(6, 0.2, 0.5);
// [0.1549, 0.2023, 0.1681, 0.0981, 0.1621, 0.2141]
Here is a jsfiddle: https://jsfiddle/brunoimbrizi/q3fLp8u5/27/
This is a partial solution:
function get_6_rands() {
var rands = [], rand, total = 0, normalized_rands = [];
for (var i = 0; i < 6; i += 1) {
rand = Math.random();
rands.push(rand);
total += rand;
}
for (var i = 0; i < 6; i += 1) {
rand = rands[i] / total;
normalized_rands.push(rand);
}
return normalized_rands;
}
function_is_valid(attempt) {
return true; // implement `is_valid` yourself
}
do {
var attempt = get_6_rands();
} while (is_valid(attempt) === false);
console.log(attempt); // success!
The function will generate 6 random numbers that, together, are 1
. It's achieved through normalization. If you want integers (rather than floats) you will need to do something clever, just rounding isn't enough.
You can get the 20%
requirement by checking the requirement, if it fails, try again, and keep trying till you get one that satisfies your requirement. You should get one eventually, it's random after all.
First you make an array of random number between 100 and 120 like that :
var arPercentages = [];
for(var i = 0; i < 6; i++) {
arPercentages.push(Math.random()*20 + 100);
}
Then you add all the numbers. Choose your method, I always user underscore so I would map(). But basically, what you have to do is something like that :
var total = arPercentages[0] + arPercentages[1] + arPercentages[2] + arPercentages[3] + arPercentages[4] + arPercentages[5];
And finally you just divide and multiply for each number :
for (var i = 0; i < 6; i++) {
arPercentages[i] = arPercentages[i] / total * 100;
}
You have an array with 6 percentages that add to 100 and that are between 100 and 120% of a base value (but this is very close)