php – 計算哪些產品在一起將提供所要求的電力
假設我有三個產品:
產品A
將提供5個電源.成本50.
產品B將提供9個電源.成本80.
產品C將提供15個電源.成本140.
當我需要7個電源時,我想知道我可以購買什麼產品的組合.我可以買到兩個A,但是B中的一個是便宜的.
當我需要65個電源我需要4次C和1次A(費用680).但我也可以去七個B產品和一個A(費用610).
我正在尋找一種方法來計算我需要的給定功率的產品的可能組合.
我這樣做的方式不會給我我想要的:
// $products are sorted DESC on their $power $power = 65 while( $power > 0 ) { foreach( $products as $productPower ) { if( ( $productPower > $power && $power - $productPower > 0 ) || $productPower == end( $products ) ) { // Add product to list $power -= $productPower; break; } } }
這個示例程式碼只會給我4次C和一次A.我該怎麼辦?
編輯產品數量是可變的.此外,具體成本和功率是可變的.所以可能會有10個產品與cheeper和更昂貴的價格標籤.
編輯2如上所述,我想計算可能的組合(複數).有些人似乎在我的描述中錯過了.
介紹
這將是一個ofollow,noindex" target="_blank">Knapsack problem ,但因為你不只是尋找最佳的解決方案,你也想找到所有可能的組合
那麼你可以解決這個Subset sum problem Coin Change 得到:
>列出所有可能的組合,而不僅僅是總組合
>獲得最佳組合
例如,對於N = 4,S = {1,2,3},有四個解:{1,1,1,1},{1,1,2},{2,2},{1, 3}.
實施例1
echo "<pre>"; $start = microtime(true); // Start Finder $finder = new CombinationFinder(65); // Add Produts $finder->addProduct(new Product("A", 5, 50)); $finder->addProduct(new Product("B", 9, 80)); $finder->addProduct(new Product("C", 15, 140)); // Output All Found Combinations foreach ( $finder as $key => $sales ) { echo $sales->getName(), "\t\t\t", $sales->getCombinationCost(), PHP_EOL; } // Get Best Combination echo "Combination: ", $finder->getBestCombination()->getName(), PHP_EOL; echo "Cost: ", number_format($finder->getBestCombination()->getCombinationCost(), 2), PHP_EOL; // Total Time echo PHP_EOL, microtime(true) - $start;
產量
頂級組合
["A",1],["C",4]610 ["A",1],["B",5],["C",1]590 ["A",4],["C",3]620 ["A",4],["B",5]600 ["A",7],["C",2]630 ["A",10],["C",1]640 ["A",13]650
最佳組合
Combination: ["A",1],["B",5],["C",1] Cost: 590.00
總時間
0.2533269405365
最佳組合
你可以看到最好的組合是A * 1,B * 5,C * 1 ..分解
ABC Power : 5*1 +9*5 +15*1=65 Cost: 50*1 +80 *5 +140 *1=590<---- Better than 610.00
實施例2
該類可以用於2,3,4或更多的產品組合,並且非常快
echo "<pre>"; $start = microtime(true); // Start Finder $finder = new CombinationFinder(65); // Add Produts $finder->addProduct(new Product("A", 5, 50)); $finder->addProduct(new Product("B", 9, 80)); $finder->addProduct(new Product("C", 15, 140)); $finder->addProduct(new Product("D", 20, 120)); // more product class $finder->run(); // just run // Get Best Combination echo "Combination: ", $finder->getBestCombination()->getName(), PHP_EOL; echo "Cost: ", number_format($finder->getBestCombination()->getCombinationCost(), 2), PHP_EOL; // Total Time echo PHP_EOL, microtime(true) - $start;
產量
Combination: ["A",1],["D",3]//<---------------------- Best Combination Cost: 410.00
所用的時間
1.1627659797668// less than 2 sec
使用類
class Product { public $name; public $power; public $cost; public $unit; function __construct($name, $power, $cost) { $this->name = $name; $this->power = $power; $this->cost = $cost; $this->unit = floor($cost / $power); } } class Sales { /** * * @var Product */ public $product; public $count; public $salePower; public $saleCost; function __construct(Product $product, $count) { $this->product = $product; $this->count = $count; $this->salePower = $product->power * $count; $this->saleCost = $product->cost * $count; } } class SalesCombination { private $combinationPower; private $combinationCost; private $combinationName; private $combinationItems; private $args; function __construct(array $args) { list($this->combinationPower, $this->combinationCost, $this->combinationItems) = array_reduce($args, function ($a, $b) { $a[0] += $b->salePower; $a[1] += $b->saleCost; $a[2] = array_merge($a[2], array_fill(0, $b->count, $b->product->name)); return $a; }, array(0,0,array())); $this->args = $args; } function getName() { $values = array_count_values($this->combinationItems); $final = array(); foreach ( $values as $name => $amount ) { $final[] = array($name,$amount); } return substr(json_encode($final), 1, -1); } function getCombinationPower() { return $this->combinationPower; } function getCombinationCost() { return $this->combinationCost; } } class CombinationFinder implements IteratorAggregate, Countable { private $sales; private $products = array(); private $power; private $found = array(); private $bestCombination = null; private $run = false; function __construct($power) { $this->power = $power; } function addProduct(Product $product) { $this->products[] = $product; } function getBestCombination() { return $this->bestCombination; } function getFound() { return $this->found ?: array(); } public function getIterator() { if ($this->run === false) { $this->run(); } return new ArrayIterator($this->found); } public function count() { return count($this->found); } function run() { $this->run = true; $this->buildSales(); $u = new UniqueCombination($this->sales); $u->setCallback(array($this,"find")); $u->expand(); } function find() { $salesCombination = new SalesCombination(func_get_args()); if ($salesCombination->getCombinationPower() == $this->power) { isset($this->bestCombination) or $this->bestCombination = $salesCombination; $salesCombination->getCombinationCost() < $this->bestCombination->getCombinationCost() and $this->bestCombination = $salesCombination; $this->found[sha1($salesCombination->getName())] = $salesCombination; } } function buildSales() { $total = count($this->products); foreach ( $this->products as $product ) { $max = floor($this->power / $product->power); for($i = 1; $i <= $max; $i ++) { $this->sales[$product->name][] = new Sales($product, $i); } } } } class UniqueCombination { private $items; private $result = array(); private $callback = null; function __construct($items) { $this->items = array_values($items); } function getResult() { return $this->result; } function setCallback($callback) { $this->callback = $callback; } function expand($set = array(), $index = 0) { if ($index == count($this->items)) { if (! empty($set)) { $this->result[] = $set; if (is_callable($this->callback)) { call_user_func_array($this->callback, $set); } } return; } $this->expand($set, $index + 1); foreach ( $this->items[$index] as $item ) { $this->expand(array_merge($set, array($item)), $index + 1); } } }
程式碼日誌版權宣告:
翻譯自:http://stackoverflow.com/questions/13211189/calculate-which-products-together-would-deliver-the-requested-power