1

I'm trying to create an array of numbers of a set length, defining the minimum and a maximum number in the set, and letting a function determine the rest of the numbers between. The kicker is that the sum of this array of numbers must be equal to a predetermined value. The trick is figuring out how that function works.

I found this on stack overflow, which got me the following function:

export const distributeValues = (amount, weights=[]) => {
    const distributedAmounts = []
    let totalWeights = weights.reduce( (a,b) => a + b)

    weights.forEach( weight => {
        const weightValue = parseFloat(weight)
        const percentage = weightValue / totalWeights
        const distributedAmount = Math.round(percentage * amount)
        distributedAmounts.push(distributedAmount)
        totalWeights -= weightValue
        amount -= distributedAmount
    })

    return distributedAmounts
}

This seems like a good start, but I actually need to work backwards; I'm trying to figure out a function that will give me the weights that would be passed into the above function.

Right now, I have this, a function broken into two parts (apologies for the redundancy):

export const getDistributions = (amount, distributions, modifier) => {
    const values = []
    let amountLeft = amount;
    for (let i = 0; i < distributions; i++ ) {
        const value = Math.max(Math.round((amountLeft / (modifier || 4))),1)
        amountLeft -= value
        values.push(value)
    }

    // -------------------------------------------- //
    // --- correct for cases where total values --- //
    // --- end up greater/less than amount      --- //
    // -------------------------------------------- //

    let iterator = 0
    let totalAssignedValue = values.reduce((a,b) => a+b);

    const lastIndex = (values.length - 1);
    const getIndex = (iterator, values) => {
        return iterator > lastIndex ? iterator % lastIndex : iterator
    }

    while (totalAssignedValue > amount) {
        iterator = getIndex(iterator)
        if (iterator !== lastIndex && iterator !== 0 && values[iterator] > 1) {
            values[iterator]--
        }
        iterator ++
        totalAssignedValue = values.reduce((a,b) => a+b);
    }
    while (totalAssignedValue < amount) {
        iterator = getIndex(iterator)
        if (iterator !== lastIndex && iterator !== 0) {
            values[iterator]++
        }
        iterator ++
        totalAssignedValue = values.reduce((a,b) => a+b);
    }

    // -------------------------------------------- //
    // -------------- end correction -------------- //
    // -------------------------------------------- //

    return values;
}

The first part tries and distributes the values, but invariably I end up with values that are greater or lesser than the input amount, so there's a second part of the equation that fixes that. Seems a little unclean though, and it's a little arbitrary how the remainders get distributed, so a pure mathematical solution would be great.

I'm starting to wonder if I'm going to need calculus for this, because I basically have the integral (the sum of the array's values), the range of the integral (min and max values), and now have to figure out the formula for the curve. This may be overkill at this point, though.

Thanks for the input!

Community
  • 1
  • 1
stubbly
  • 11
  • 1
  • One thing you'll need to know is about how JavaScript stores numbers, which can mess up calculations (e.g., 0.1 + 0.2 !== 0.3). See for example [this page, which explains some about the issue](https://modernweb.com/what-every-javascript-developer-should-know-about-floating-points/). – Heretic Monkey Jan 19 '17 at 22:41
  • Great call.. thank you! – stubbly Jan 19 '17 at 22:46
  • It would help greatly if you include example input and expected output. You say you want to specify the minimum and maximum values, how many there are and the sum, but there don't seem to be parameters for them. – RobG Jan 19 '17 at 23:03
  • Basically I'm trying to get a distribution of percentages. So, the sum has to equal 100, and the minimum I want is 1%. I've been using 50% as the max just to test, but it can really be anything. So, the idea would be getting, for example, a set of 5 different percentages, min = 1%, max = 50%, with the remaining 3 anything between, as long as the set sums out to 100% – stubbly Jan 23 '17 at 19:38

1 Answers1

0

How about this? First create the set in such way that the first member is the minimum, the second member is minimum + 1, the third minimum + 2, etc. Then sum up the numbers in the set and subtract the sum from the predetermined value. Then distribute the result of the subtraction among all the numbers in the set as outlined betlow.

Set makeSet(int preDet, int min, int max, int setLength)
{
    if((max + max - setLength + 1) * setLength / 2 < preDet) return null;
    if((min + min + setLength - 1) * setLength / 2 > preDet) return null;
    Set set = Set(setLength);
    int val = min;
    for (int i = 0; i < setLength; i++)
    {
        set[i] = val++;
    }
    int sum = (min + val - 1) * setLength / 2;
    int dev = preDet - sum;
    if(dev)
    {
        int adj = dev / setLength;
        if(dev % setLength) adj++;
        for(int i = setLength -1; dev; i--)
        {
            if(adj > dev) adj = dev;
            set[i] += adj;
            dev -= adj;
        }
    }
    return set;
}
Shiping
  • 1,203
  • 2
  • 11
  • 21
  • Here the set created may not contain the minimum and maximum. If the set must contain the minimum and maximum, you can just first shorten setLength by 2, increase min by 1, decrease max by 1 and subtract min and max from preDet, then go through the same process. At the end, just add min and max into the result set. – Shiping Jan 20 '17 at 13:48