Skip to content

Instantly share code, notes, and snippets.

@liorgonnen
Last active August 29, 2015 14:17
Show Gist options
  • Save liorgonnen/4df895734a2626d1d513 to your computer and use it in GitHub Desktop.
Save liorgonnen/4df895734a2626d1d513 to your computer and use it in GitHub Desktop.
/**
* Figure the ideal distribution between groups of items given the number of items in each group a
* @param weights The ideal weight distribution between the groups
* @param groupItems How many items are available in each group
* @param maxItems The max number of items to distribute
* @return A sequence containing the number of items in each group according to the requested distribution ratios
*/
def distribute(weights: Seq[Float], groupItems: Seq[Int], maxItems: Int): Seq[Int] = {
require(weights != null, "null weights")
require(groupItems != null, "null groupItems")
require(weights.length == groupItems.length, "weights.length <> groupItems.length")
require(weights.length > 0, "Need at least one group to distribute")
@tailrec def subDistribute(ratios: Seq[Float], items: Seq[Int], maxItems: Int, accumulatedResult: Seq[Int]): Seq[Int] = {
if (items.length == 1) return accumulatedResult :+ (items(0) min maxItems)
val totalAvailableItems: Float = items.sum min maxItems
val itemsToTake = (ratios(0) * totalAvailableItems).toInt min items(0)
val unusedRatio = ratios(0) - itemsToTake / totalAvailableItems
val distributedUnusedRatio = unusedRatio / (items.length - 1)
subDistribute(
ratios.drop(1).map(_ + distributedUnusedRatio),
items.drop(1),
maxItems - itemsToTake.toInt,
accumulatedResult :+ itemsToTake
)
}
val positiveWeights = weights.filter(_ > 0)
val totalWeight = positiveWeights.sum
val weightToDistribute = ((1f - totalWeight) max 0f) / (weights.length - positiveWeights.length)
val adjustedWeights = weights.map(w => if (w > 1f) 1f else if (w > 0) w else weightToDistribute)
val (ratiosAndItems, order) = (adjustedWeights zip groupItems zip (0 until groupItems.length)).sortBy { case ((ratio, items), order) => items / (maxItems * ratio) }.unzip
val (ratios, items) = ratiosAndItems.unzip
val unorderedResult = subDistribute(ratios, items, maxItems, Seq.empty)
(unorderedResult zip order).sortBy { case (v, order) => order }.unzip._1
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment