Last active
August 29, 2015 14:17
-
-
Save liorgonnen/4df895734a2626d1d513 to your computer and use it in GitHub Desktop.
This file contains 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
/** | |
* 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