Skip to content

Instantly share code, notes, and snippets.

@dcabanaw
Last active September 3, 2015 20:44
Show Gist options
  • Save dcabanaw/eb39ec308a1861aff398 to your computer and use it in GitHub Desktop.
Save dcabanaw/eb39ec308a1861aff398 to your computer and use it in GitHub Desktop.
naive attempt at a 0-1 knapsack type solution... Am I the coding horror?
<?php
/**
* Class BinPool
* simple implementation of the 0-1 Knapsack problem, with multiple knapsacks (bins)
*
* Given a pool of item sizes/weights, a bin size and a quantity of bins, can the pool be fully allocated to the bins
* and have room to add another item
*/
class BinPool
{
/**
* @var integer
*/
private $bin_size;
/**
* @var integer
*/
private $bin_quantity;
/**
* @var array
*/
private $pool;
/**
* @var array
*/
private $bins = array();
/**
* @param integer $bin_size the size of each Bin
* @param integer $bin_quantity how many Bins
* @param array $pool an array of numeric sizes/weights
*/
public function __construct($bin_size, $bin_quantity, array $pool = array())
{
$this->bin_size = $bin_size;
$this->bin_quantity = $bin_quantity;
$this->pool = $pool;
}
/**
* @param $item_size
*
* @return bool
*/
public function isAddable($item_size)
{
if ($this->isItemLargerThanBinSize($item_size) || $item_size == 0)
{
return false;
}
if ($this->isPoolFullyAllocated())
{
return false;
}
$this->validatePool();
$this->buildBins();
if ($this->doesItemFitIntoLastBin($item_size) && empty($this->pool))
{
return true;
}
return false;
}
private function validatePool()
{
if (!empty($this->pool))
{
if (max($this->pool) > $this->bin_size)
{
throw new RuntimeException("An item in the pool exceeds the max size of {$this->bin_size}");
}
}
}
private function buildBins()
{
for ($i = 0; $i < $this->bin_quantity; $i++)
{
$this->bins[$i] = array();
rsort($this->pool);
while ($this->isBinNotFull($i) && !empty($this->pool))
{
$this->shiftItemFromPoolIntoBin($i);
if ($this->isBinOverFilled($i))
{
$this->popItemFromBinToPool($i);
}
if ($this->smallestItemDoesNotFitInBin($i))
{
break;
}
if ($this->isBinFull($i))
{
break;
}
}
}
}
/**
* @param $i
*
* @return bool
*/
private function smallestItemDoesNotFitInBin($i)
{
return !empty($this->pool) && min($this->pool) + array_sum($this->bins[$i]) > $this->bin_size;
}
/**
* @param $i
*
* @return bool
*/
private function isBinFull($i)
{
return array_sum($this->bins[$i]) == $this->bin_size;
}
/**
* @param $i
*
* @return bool
*/
private function isBinNotFull($i)
{
return array_sum($this->bins[$i]) <= $this->bin_size;
}
/**
* @param $i
*/
private function shiftItemFromPoolIntoBin($i)
{
$this->bins[$i][] = array_shift($this->pool);
}
/**
* @param $i
*
* @return bool
*/
private function isBinOverFilled($i)
{
return array_sum($this->bins[$i]) > $this->bin_size;
}
/**
* @param $i
*
* @return mixed
*/
private function popItemFromBinToPool($i)
{
$this->pool[] = array_pop($this->bins[$i]);
}
/**
* @param $item
*
* @return bool
*/
private function isItemLargerThanBinSize($item)
{
return $item > $this->bin_size;
}
/**
* @return bool
*/
private function isPoolFullyAllocated()
{
return array_sum($this->pool) >= $this->bin_size * $this->bin_quantity;
}
/**
* @param $item
*
* @return bool
*/
private function doesItemFitIntoLastBin($item)
{
return array_sum($this->bins[count($this->bins) - 1]) + $item <= $this->bin_size;
}
}
<?php
$pool = [1,1,1,2,2];
$bp = new BinPool(4, 2, $pool);
$bp->isAddable(1); //true
$pool = [1,1,2,2,2];
$bp = new BinPool(4, 2, $pool);
$bp->isAddable(1); //false
@dcabanaw
Copy link
Author

dcabanaw commented Sep 3, 2015

okay this is pretty awful... I think i did better here:
https://gist.github.com/dcabanaw/f7df7d36f9cfa5763a4f

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment