cjh193
cjh193

Reputation: 359

How to divide number n in javascript into x parts, where the sum of all the parts equals the number?

I have a number which I need to divide into 5 parts. However, I want each part to be a random number. But when all the parts are added together, they equal the original number. I am unsure of how to do this with JavaScript. Furthermore, I don't want the min of the divided parts to be 0 or 1, I want to set the min myself.

For example, the number is 450. I want the divided parts to be no less than 60. So to start, the array would be [60,60,60,60,60]. But I want to randomize so that they all add up to 450. What would be the best way to go about doing this?

Thank you!

This is what I've tried so far:

let i = 0;
let number = 450;
let numArray = [];
while(i <= 5){
  while(number > 0) {
    let randomNum = Math.round(Math.random() * number) + 1;
    numArray.push(randomNum);
    number -= randomNum;
  }
  i += 1;
}

Upvotes: 5

Views: 12001

Answers (7)

Lurk Addams
Lurk Addams

Reputation: 11

The highest rated answer doesn't include the minimum variable, so here are some adjustments that I've made:

function* splitNParts(num, parts, min) {
    let remParts = parts;
    let sumParts = num;
    for (let i = 0; i < parts - 1; i++) {
        const pn = Math.ceil(Math.random() * (sumParts - (remParts*min)) + min)
        yield pn
        sumParts -= pn;
        remParts -= 1;
    }
    yield sumParts;
}

console.log(...splitNParts(450, 5, 60));

Upvotes: 1

smac89
smac89

Reputation: 43206

let your number be N, and let pn be the nth part. To get 5 parts:

  • p1 = random number between 0 and N
  • p2 = random number between 0 and N - p1
  • p3 = random number between 0 and N - p2 - p1
  • p4 = random number between 0 and N - p3 - p2 - p1
  • p5 = N - p4 - p3 - p2 - p1

Edit 2017

To make it seem more random, shuffle the numbers after you generate them

Edit 2020

I guess some code wouldn't hurt. Using ES7 generators:

function* splitNParts(num, parts) {
    let sumParts = 0;
    for (let i = 0; i < parts - 1; i++) {
        const pn = Math.ceil(Math.random() * (num - sumParts))
        yield pn
        sumParts += pn
    }
    yield num - sumParts;
}

Fiddle Link

Upvotes: 6

Stephen R. Smith
Stephen R. Smith

Reputation: 3400

There are much more efficient ways to code this, but in the interest of explaining the logic of solving the problem, this walks through that step by step, transforming the values and explaining the logic.

// Set start number, number of fragments
// minimum fragment size, define fragments array

var n = 450
var x = 5
var minNumber = 60
var fragment = n / x

// stuff array with equal sized fragment values
var fragments = []
for (i = 0; i < x; i++) {
  fragments[i] = fragment;
}

document.write("fragments: " + fragments);

var delta = [];

// iterate through fragments array
// get a random number each time between the fragment size
// and the minimum fragment sized defined above
// for even array slots, subtract the value from the fragment
// for odd array slots, add the value to the fragment
// skip the first [0] value
for (i = 1; i< x; i++) {
  delta[i] = Math.floor(Math.random() * (fragment - minNumber));
  document.write("<br />delta: " + delta[i]);
  if((i % 2) == 1) {
    fragments[i] -= delta[i]
  }
  else {
    fragments[i] += delta[i]
  }
}

// set the initial fragment value to 0
fragments[0] = 0

// defines a function we can use to total the array values
function getSum(total, num) {
    return total + num;
}

// get the total of the array values, remembering the first is 0
var partialTotal = fragments.reduce(getSum)
document.write("<br />partial sum: " + partialTotal);

// set the first array value to the difference between
// the total of all the other array values and the original
// number the array was to sum up to
fragments[0] = (n - partialTotal)

// write the values out and profit.
document.write("<br />fragments: " + fragments);
var grandTotal = fragments.reduce(getSum)
document.write("<br />Grand total: " + grandTotal);

https://plnkr.co/edit/oToZe7LGpQS4dIVgYHPi?p=preview

Upvotes: 0

guest271314
guest271314

Reputation: 1

You can use a do..while loop to subtract a minimum number from original number, keep a copy of original number for subtraction at conclusion of loop to push the remainder to the array

let [n, total, m = n] = [450, 0];
const [min, arr] = [60, []];
do {
  n -= min; // subtract `min` from `n`
  arr.push(n > min ? min : m - total); // push `min` or remainder 
  total += arr[arr.length - 1]; // keep track of total
} while (n > min);

console.log(arr);

To randomize output at resulting array select a number greater than min and less than n to create a random number within a specific range

let [n, total, m = n] = [450, 0];
const [min, arr, range = min + min / 2] = [60, []];

do {
  let r = Math.random() * (range - min) + min; // random number in our range
  n -= r; // subtract `min` from `n`
  arr.push(n > min ? r : m - total); // push `r` or remainder 
  total += arr[arr.length - 1]; // keep track of total
} while (n > min);

console.log(arr);

Upvotes: 2

Siphalor
Siphalor

Reputation: 723

Sum the five minimums (eg min = 60) up:

var minSum = 5 * min

Then get the difference between your original number (orNumber = 450) and minSum.

var delta = orNumber - minSum

Now you get 4 different random numbers in the range from 0 to exclusive 1.

Sort these numbers ascending.

Foreach of these randoms do the following:

  • Subtract it from the last one (or zero for the first)
  • Multiply this number with the delta and you get one of the parts.

The last part is the delta minus all other parts.

Afterwards you just have to add your min to all of the parts.

Upvotes: 5

James
James

Reputation: 22246

This function generates random numbers from 0 to 1, adds them together to figure out what they need to be multiplied by to provide the correct range. It has the benefit of all the numbers being fairly distributed.

function divvy(number, parts, min) {

  var randombit = number - min * parts;
  var out = [];
  
  for (var i=0; i < parts; i++) {
    out.push(Math.random());
  }
  
  var mult = randombit / out.reduce(function (a,b) {return a+b;});
  
  return out.map(function (el) { return el * mult + min; });
}
var d = divvy(450, 6, 60)
console.log(d);
console.log("sum - " + d.reduce(function(a,b){return a+b}));

Upvotes: 3

plumpNation
plumpNation

Reputation: 162

I made a longer version for beginners.

const n          = 450;
const iterations = 5;
const parts      = [];

// we'll use this to store what's left on each iteration
let remainder = n;

for (let i = 1; i <= iterations; i += 1) {
    // if it's the last iteration, we should just use whatever
    // is left after removing all the other random numbers
    // from our 450
    if (i === iterations) {
        parts.push(remainder);

        break;
    }

    // every time we loop, a random number is created.
    // on the first iteration, the remainder is still 450
    const part = Math.round(Math.random() * remainder);

    parts.push(part);

    // we must store how much is left after our random numbers
    // are deducted from our 450. we will use the lower number
    // to calculate the next random number
    remainder -= part;
}

// let's print out the array and the proof it still adds up
const total = totalFromParts(parts);

console.log(parts);
console.log('Total is still ' + total);

// this function loops through each array item, and adds it to the last
// just here to test the result
function totalFromParts(parts) {
    return parts.reduce((sum, value) => sum + value, 0);
}

Upvotes: 0

Related Questions