Created
November 28, 2012 06:53
-
-
Save wilwade/4159496 to your computer and use it in GitHub Desktop.
This function will take a list of items of various sizes and sort them into buckets as close as possible to even, while maintaining the original order.
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
<?php | |
/** | |
* This function will take a list of various "sizes" and sort them into buckets | |
* as close as possible to even, while maintaining the original order. | |
* | |
* This is basically a justify all lines sort of algorithum | |
* | |
* Example: | |
* $array = array('2','4','2','1','1','2','3','1','3','2','3',); | |
* var_dump(createBuckets($array, 6)); | |
* Outputs: | |
* array | |
* 0 => | |
* array | |
* 0 => string '2' | |
* 1 => string '4' | |
* 1 => | |
* array | |
* 2 => string '2' | |
* 3 => string '1' | |
* 4 => string '1' | |
* 5 => string '2' | |
* 2 => | |
* array | |
* 6 => string '3' | |
* 7 => string '1' | |
* 3 => | |
* array | |
* 8 => string '3' | |
* 9 => string '2' | |
* 4 => | |
* array | |
* 10 => string '3' | |
* | |
* @throws Exception (if an block has a size greater than the bucketSize) | |
* | |
* @author Wil Wade <william@willmwade.com> | |
* @version 1.0 | |
* @license GPL3 | |
* | |
* @param array $blocks Indexed array of the bucket sizes | |
* @param int $bucketSize The maximum size of each bucket | |
* @return array | array(0 => array(blockIndex => blockSize, ...), ...) | |
*/ | |
function createBuckets($blocks, $bucketSize = 6){ | |
//Start our bucket array | |
$buckets = array(0 => array()); | |
$currentBucket = 0; | |
//Go ahead and put the blocks into buckets in order, but never larger than the $bucketSize | |
foreach($blocks as $blockIndex => $blockSize){ | |
if($blockSize > $bucketSize){ | |
throw new Exception("Block Index {$blockIndex} is greater than the bucket size of {$bucketSize}."); | |
} | |
$currentBucketLevel = array_sum($buckets[$currentBucket]); | |
//Test to see if we can add it to the current bucket | |
if($currentBucketLevel + $blockSize > $bucketSize){ | |
$currentBucket++; | |
} | |
$buckets[$currentBucket][$blockIndex] = $blockSize; | |
} | |
//This is the number of buckets we have. | |
//There is really no way this will be more or less. | |
$bucketCount = count($buckets); | |
//If we only have one bucket, just return it | |
if($bucketCount === 1) | |
return $buckets; | |
//Lower optimal size of each bucket | |
$bestBucketSize = floor( array_sum($blocks) / $bucketCount ); | |
//Ok time to even things out. | |
//We start by working backwords, because we want to weight our larger | |
// to the top if possible, but we do not iterate over the top one ($i > 0) | |
for($i = $bucketCount - 1; $i > 0; $i--){ | |
$currentBucketLevel = array_sum($buckets[$i]); | |
if($currentBucketLevel < $bestBucketSize){ | |
//Ok we need to get some if possible from the previous bucket | |
//Remember not to go > the $bestBucketSize | |
$prevLastBlock = end($buckets[$i - 1]); | |
while($prevLastBlock + $currentBucketLevel <= $bestBucketSize){ | |
//Get the key from the end($buckets[$i - 1]) to maintain indexing | |
$prevLastBlockKey = key($buckets[$i - 1]); | |
//This takes it off of one array and adds it to the current | |
$buckets[$i][$prevLastBlockKey] = $prevLastBlock; | |
unset($buckets[$i - 1][$prevLastBlockKey]); | |
$currentBucketLevel = array_sum($buckets[$i]); | |
$prevLastBlock = end($buckets[$i - 1]); | |
} | |
} | |
ksort($buckets[$i]); | |
} | |
return $buckets; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment