Proportional Percentage Scaling

percentages

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/

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.

Related Question