Skip to content

Instantly share code, notes, and snippets.

@wilwade
Created November 28, 2012 06:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wilwade/4159496 to your computer and use it in GitHub Desktop.
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.
<?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