Created
September 10, 2018 01:01
-
-
Save drzhbe/2f7083d79bedbf8fbd4d51bc53ad1c42 to your computer and use it in GitHub Desktop.
Largest remainder method of fixing 101%
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** Scales values to be a percentage of their sum. E.g., [20, 30] -> [40, 60] */ | |
function scaleToPercentages(rows: Row[]): Row[] { | |
rows.forEach(([_, vs]) => Preconditions.checkArgument(vs.length === 1)); // Only supports one | |
// column. | |
if (rows.length === 0) { | |
return rows; | |
} | |
const sum = rows | |
.map(([_, [v]]) => v) | |
.reduce((acc, v) => acc + v, 0); | |
const percents: Row[] = rows.map(([l, vs]) => ([ | |
l, | |
vs.map(v => (v / sum) * 100), | |
] as Row)); | |
// According to the "Largest remainder method" (to avoid "101%" case): | |
// 1. Take floored percentage values | |
// 2. Calc remaining percents | |
// 3. Distribute them between the largest remainder values | |
let remainingPercents = 100; | |
const remainders: [number, number][] = []; // [index, remainder][] | |
for (let i = 0; i < percents.length; i++) { | |
const [, [decimal]] = percents[i]; | |
const integer = Math.floor(+decimal); | |
remainingPercents -= integer; | |
remainders[i] = [i, +decimal - integer]; | |
percents[i][1] = [integer]; | |
} | |
// Distribute remaining percents between the highest remainder values. | |
remainders.sort((a, b) => a[1] === b[1] | |
? a[0] - b[0] // save position if values are equal by comparing indexes | |
: b[1] - a[1]); | |
for (let i = 0; i < remainingPercents; i++) { | |
const [index] = remainders[i]; | |
const [, [value]] = percents[index]; | |
percents[index][1] = [+value + 1]; | |
} | |
return percents; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment