Skip to content

Instantly share code, notes, and snippets.

@filiph
Last active March 2, 2020 10:09
Show Gist options
  • Save filiph/cea429a0404f3a23f69c to your computer and use it in GitHub Desktop.
Save filiph/cea429a0404f3a23f69c to your computer and use it in GitHub Desktop.
Google Apps Script custom function (https://developers.google.com/apps-script/guides/sheets/functions) that takes an integer count and a partition (array of percentages that sum to 1.0) and distributes the count according to the partition. Returns an array of integers (the restricted weighted integer composition). Works well for very small value…
// Takes [count] of items and the [partition] (array of percentages) to
// distribute against. Returns an array of integers that sums to [count]:
// an integer composition weighted by the partition.
//
// We don't use the Bresenham algorithm because it doesn't provide the best
// results for low values of [count].
function DISTRIBUTE(count, partition) {
// Custom function in Apps Script takes a 2D array. We're only interested in
// the first row.
partition = partition[0];
// First, distribute rounded down, but keep the remainders for each bin.
var result = [];
var remainders = [];
var alreadyDistributedCount = 0;
for (var i = 0; i < partition.length; i++) {
var currentCount = Math.floor(partition[i] * count);
var remainder = partition[i] * count - currentCount;
result.push(currentCount);
remainders.push(remainder);
alreadyDistributedCount += currentCount;
}
// Now iteratively add [partition] values to corresponding remainders.
var j = 0;
while (alreadyDistributedCount < count) {
remainders[j] += partition[j];
if (remainders[j] > 1) {
result[j] += 1;
remainders[j] -= 1;
alreadyDistributedCount += 1;
}
j += 1;
if (j >= partition.length) j = 0;
}
// Apps Script expects 2D array output. We only provide one row.
return [result];
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment