Proportional Percentage Scaling

1.7k Views Asked by At

I've been breaking my head in trying to calculate the proportional scaling of percentages, in which the end result should always quantify to 100%.

For example:

Person A - 60%
Person B - 40%

When adding person C with 50%, the following scaling occurs, in proportion to their original percentage of what's left:

Person A: 30%
Person B: 20%
Person C: 50%

When amending an entry; for example, setting Person A to 10%, the following occurs:

Person A: 10%
Person B: 30%
Person C: 60%

I was able to calculate this off the top of my head (strange - that never works), however I'm uncertain as to how to apply this in mathematical formula, and in its simplest form; in such a way that the calculation works in all scenarios, whether adding, amending or removing persons and their percentages.

At every calculation, I have existing reference to the following:

  • How many items (persons) there currently are
  • The original percentage allocated to that person

Your help would be greatly appreciated.

Edit

I'm not sure how to explain it in a better form, but I hope the following tables may explain a bit better:

Where ? are the values to be calculated

The closest I've come so far:

var beneficiaries = [
    100
]; // Current state of beneficiaries

const calculateBeneficiaryRatio = (action, value = 0, index = 0) => {
    let results = [];
  let cur_percentage, new_percentage;
  
    switch(action) {
    case "add":
            for(let i in beneficiaries) {
          cur_percentage = beneficiaries[i];
          new_percentage = (100 - value) * (cur_percentage / 100);
          beneficiaries[i] = new_percentage;
        } 
            beneficiaries.push(value);
        return beneficiaries;
        break;
    case "remove":
            value = beneficiaries[index];
        beneficiaries.splice(index, 1);
            for(let i in beneficiaries) {
          cur_percentage = beneficiaries[i];
          new_percentage = cur_percentage + (value / beneficiaries.length);
          beneficiaries[i] = new_percentage;
        }
        return beneficiaries;
        break;
    case "update":
            beneficiaries[index];
            for(let i in beneficiaries) {
          cur_percentage = beneficiaries[i];
          new_percentage = (100 - value) * (cur_percentage / 100) + (value / beneficiaries.length);
          beneficiaries[i] = new_percentage;
        }
        return beneficiaries;
        break;
  }
}

beneficiaries = calculateBeneficiaryRatio('add', 29);
console.log(beneficiaries);
beneficiaries = calculateBeneficiaryRatio('add', 20);
console.log(beneficiaries);
beneficiaries = calculateBeneficiaryRatio('remove', null, 1);
console.log(beneficiaries);
beneficiaries = calculateBeneficiaryRatio('update', 20, 0);
console.log(beneficiaries);

https://jsfiddle.net/74Lf2dhk/1/

1

There are 1 best solutions below

9
On BEST ANSWER

Neither of your two examples matches your description: instead of $(85,5,10)$, I would have expected $(81,9,10)$, because that preserves the ratio $a:b$, i.e., $90:10=81:9$. Maybe there was a miscalculation?

If that's the case, then here's the idea: suppose you have some percentages $(a,b,c,d)$ and you're changing $d$ to $d+\Delta$. Then we need to remove a total of $\Delta$ from the other three while preserving the ratio $a:b:c$.

Looking at $a$ as a fraction of $a+b+c$, we have $\frac a{a+b+c}$. This is the fraction of $\Delta$ by which $a$ must decrease, so the new value of $a$ will be $$a - \left(\frac a{a+b+c}\right)\Delta,$$ which after factoring out the $a$ we can rewrite as $$a\left(1 - \frac \Delta{a+b+c}\right).$$

To put it more simply, let $T$ be the total of the first three values: $T=a+b+c$. Then the new value of $a$ will be $a\left(\frac{T-\Delta}T\right).$

The changes to $b$ and $c$ are similar; therefore, the new values of $(a,b,c,d)$ will be $$\left(a\left(\frac{T-\Delta}T\right), b\left(\frac{T-\Delta}T\right), c\left(\frac{T-\Delta}T\right), d + \Delta\right).$$

Finally, note that the case $a=b=c=0$ will have to be given special treatment.