Php 计算哪些产品一起可以提供所需的功率
假设我有三种产品:Php 计算哪些产品一起可以提供所需的功率,php,algorithm,optimization,Php,Algorithm,Optimization,假设我有三种产品: products = [Product(i, i, i-i/100) for i in range(1000)] solve(products, 12345) """ solution time: 0.0922736688601 1 45.0 100 123.0 power = 123*100 + 45*1 =12345 """ 产品A 将提供5个电源。50美元 产品B将提供9倍功率。80美元 产品C将提供15倍功率。140美元 我想知道当我需要7倍动力时,我可以购买什
products = [Product(i, i, i-i/100) for i in range(1000)]
solve(products, 12345)
"""
solution time: 0.0922736688601
1 45.0
100 123.0
power = 123*100 + 45*1 =12345
"""
产品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种产品带有幼儿和更昂贵的价格标签
编辑2如上所述,我想计算可能的组合(复数)。有些人在我的描述中似乎忽略了这一点。您想优化以下功能吗
$cost = $amountOfProductA * $costOfProductA + $amountOfProductB * $costOfProductB + $amountOfProductC * $costOfProductC
有以下限制
$powerDeliveredByA * $amountOfProductA + $powerDeliveredByB * $amountOfProductB + $powerDeliveredByC * $amountOfProductC = 65
因此,这些行会找到产量为65(或接近65,使用您必须设置的可接受阈值)的解决方案,然后按成本对解决方案数组进行排序,并获得解决方案数组的第一个元素:
$requiredPower = 65;
$productA = array('amount' => 0, 'cost' => 50, 'powerDelivered' => 5);
$productB = array('amount' => 0, 'cost' => 80, 'powerDelivered' => 9);
$productC = array('amount' => 0, 'cost' => 140, 'powerDelivered' => 15);
$increment = 0.01;
$threshold = 0.01;
$solutions = array();
while($productA['amount'] * $productA['powerDelivered'] < $requiredPower)
{
$productC['amount'] = 0;
while($productB['amount'] * $productB['powerDelivered'] < $requiredPower)
{
$productC['amount'] = 0;
while($productC['amount'] * $productC['powerDelivered'] < $requiredPower)
{
if($productA['amount'] * $productA['powerDelivered'] + $productB['amount'] * $productB['powerDelivered'] + $productC['amount'] * $productC['powerDelivered'] > $requiredPower + $threshold)
{
break;
}
if(isWithinThreshold($productA['powerDelivered'] * $productA['amount'] + $productB['powerDelivered'] * $productB['amount'] + $productC['powerDelivered'] * $productC['amount'], $requiredPower, $threshold))
{
//var_dump($productA['powerDelivered'] * $productA['amount'] + $productB['powerDelivered'] * $productB['amount'] + $productC['powerDelivered'] * $productC['amount']);
$cost = $productA['amount'] * $productA['cost'] + $productB['amount'] * $productB['cost'] + $productC['amount'] * $productC['cost'];
$solutions[number_format($cost,10,'.','')] = array('cost' => $cost, 'qA' => $productA['amount'], 'qB' => $productB['amount'], 'qC' => $productC['amount']);
}
$productC['amount'] = $productC['amount'] + $increment;
}
$productB['amount'] = $productB['amount'] + $increment;
}
$productA['amount'] = $productA['amount'] + $increment;
}
ksort($solutions, SORT_NUMERIC);
$minimumCost = array_shift($solutions);
var_dump($minimumCost);
//checks if $value1 is within $value2 +- $threshold
function isWithinThreshold($value1, $value2, $threshold)
{
if($value1 >= $value2 - $threshold && $value1 <= $value2 + $threshold)
{
return true;
}
}
$requiredPower=65;
$productA=array('amount'=>0,'cost'=>50,'POWERPERIED'=>5);
$productB=array('amount'=>0,'cost'=>80,'POWERPERIED'=>9);
$productC=array('amount'=>0,'cost'=>140,'POWERPERIED'=>15);
$increment=0.01;
$threshold=0.01;
$solutions=array();
而($productA['amount']*$productA['powerDelivered']<$requiredPower)
{
$productC['amount']=0;
而($productB['amount']*$productB['powerDelivered']<$requiredPower)
{
$productC['amount']=0;
而($productC['amount']*$productC['powerDelivered']<$requiredPower)
{
如果($productA['amount']*$productA['powerDelivered']+$productB['amount']*$productB['powerDelivered']+$productC['amount']*$productC['powerDelivered']>$requiredPower+$threshold)
{
打破
}
如果(在阈值内($productA['powerDelivered']*$productA['amount']+$productB['powerDelivered']*$productB['amount']+$productC['powerDelivered']*$productC['amount'],$requiredPower,$threshold))
{
//var_dump($productA['powerDelivered']*$productA['amount']+$productB['powerDelivered']*$productB['amount']+$productC['powerDelivered']*$productC['amount']);
$cost=$productA['amount']*$productA['cost']+$productB['amount']*$productB['cost']+$productC['amount']*$productC['cost'];
$solutions[number_格式($cost,10,'.','')=array('cost'=>$cost,'qA'=>$productA['amount'],'qB'=>$productB['amount'],'qC'=>$productC['amount']);
}
$productC['amount']=$productC['amount']+$increment;
}
$productB['amount']=$productB['amount']+$increment;
}
$productA['amount']=$productA['amount']+$increment;
}
ksort($solutions,SORT\u NUMERIC);
$minimumCost=阵列移位($solutions);
var_转储(最低成本);
//检查$value1是否在$value2+-$threshold范围内
函数IsWithThreshold($value1、$value2、$threshold)
{
如果($value1>=$value2-$threshold&&$value1对这个特定问题的简单观察可能有助于人们解决这个问题。电源和成本在这里的分配方式。使用产品B可以让您的钱获得最大的价值。事实上,您使用产品C的唯一时间是在您需要15电源或28-30电源的时候
因此,对于大于30所需的任何幂,只需使用整数除法即可获得所需乘积B的#,方法是:
int num_productB = power_needed/9;
int leftover = power_needed % 9;
然后通过以下方式了解您需要多大功率:
int num_productB = power_needed/9;
int leftover = power_needed % 9;
如果剩余量大于5,只需再添加一个产品B,否则使用1个产品A:
if(leftover > 5)
num_productB++;
else
productA = 1;
完整的函数如下所示:
function computeBestCombination($power_needed){
$power_results = array();
//index 0 = Product A
//index 1 = Product B
//index 2 = Product C
if($power_needed == 15){
$power_results[0] = 0;
$power_results[1] = 0;
$power_results[2] = 1;
}
else if($power_needed >= 28 && $power_needed <= 30)
$power_results[0] = 0;
$power_results[1] = 0;
$power_results[2] = 2;
else{
$power_results[1] = $power_needed / 9;
$left_over = $power_needed % 9;
if($left_over > 5){
$power_results[1]++;
}
else{
$power_results[0] = 1;
}
$power_results[2] = 0;
}
return $power_results;
}
函数计算测试组合($power\u需要){
$power_results=array();
//指数0=产品A
//指数1=产品B
//指数2=产品C
如果($power_needed==15){
$power_结果[0]=0;
$power_results[1]=0;
$power_结果[2]=1;
}
否则,如果($power\u needed>=28&&$power\u needed 5){
$power_结果[1]+;
}
否则{
$power_结果[0]=1;
}
$power_结果[2]=0;
}
返回$power\u结果;
}
检查此代码:
<?php
$products = array(5 => 50, 9 => 80, 15 => 140);
$power = 65;
$output = array();
function calculate_best_relation($products, $power, &$output) {
$aux = array_keys($products);
sort($aux);
$min = $aux[0];
if ($power <= $min) {
$output[] = $min;
return $output;
}
else {
//Calculate best relation
$relations = array();
foreach ($products as $p => $c) {
$relations[$p] = $c / $p;
}
asort($relations);
foreach($relations as $p => $c) {
if ($power > $c) {
$output[] = $p;
$power -= $c;
calculate_best_relation($products, $power, $output);
break;
}
}
}
}
calculate_best_relation($products, $power, $output);
print_r($output);
?>
这将打印:
排列
(
[0] => 9
[1] => 9
[2] => 9
[3] => 9
[4] => 9
[5] => 9
[6] => 9
[7] => 5
)
这是正确的解决方案
p.D:当然你可以优化这个功能。更新的答案
我支持我最初的答案,但后来得到了一个明确的解决方案。不幸的是,我不精通PHP,所以我将介绍的实现是用(写得不好的)F#实现的
让你的问题变得有趣的一点是,你不是在寻找最好的解决方案,而是在寻找所有可行的解决方案。正如我在原始答案中指出的,这很棘手,因为可行的解决方案集是无限的。举个例子,如果你想生产65个单位,你可以使用13xA,它产生5x13=65的幂那么,显然,任何含有超过13个单位A的溶液也将是溶液
不能从函数返回无限集。这里需要的是所有“边界”情况的集合:
- 如果解决方案包含的单元数与所有产品的边界情况相同,则该解决方案有效
- 如果一个单元可以从
__author__ = 'Robert'
import pulp
def get_lp_problem(products, required_power):
prob = pulp.LpProblem("MyProblem", pulp.LpMinimize)
total_cost = []
total_power = []
for product in products:
var = pulp.LpVariable(product.name,
lowBound=0,
upBound=None,
cat=pulp.LpInteger)
total_cost.append(var * product.cost)
total_power.append(var * product.power)
prob += sum(total_power) >= required_power #ensure we have required power
prob += sum(total_cost) #minimize total cost!
return prob
def solve(products, required_power):
lp_prob = get_lp_problem(products, required_power)
lp_prob.solve()
print lp_prob.solutionTime #0.01 seconds
for var in lp_prob.variables():
print var.name, var.varValue
from collections import namedtuple
Product = namedtuple("Product", "name, power, cost")
products = [
Product('A', 5, 50),
Product('B', 9, 80),
Product('C', 15, 140)
]
solve(products, 7)
"""
A 0.0
B 1.0
C 0.0
cost = 0*50 + 1*80 + 0*140 = 80
power = 0*5 + 1*9 + 0*15 = 9
"""
solve(products, 65)
"""
A 1.0
B 5.0
C 1.0
cost = 1*50 + 5*80 + 1*140 = 590
power = 1*5 + 5*9 + 1*15 = 65
"""
products = [Product(i, i, i-i/100) for i in range(1000)]
solve(products, 12345)
"""
solution time: 0.0922736688601
1 45.0
100 123.0
power = 123*100 + 45*1 =12345
"""
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 B C
Power : 5 * 1 + 9 * 5 + 15 * 1 = 65
Cost : 50 * 1 + 80 * 5 + 140 * 1 = 590 <---- Better than 610.00
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);
}
}
}
C(5) = 50
C(9) = 80
C(15) = 140
C(p) = Min(C(p-5) + 50, C(p-9) + 80, C(p-15) + 140)
// Create an array big enough to hold elements 0 through p inclusive.
var solution = new Array(p+1);
// Initialize the array with the base cases.
for each base case b:
solution[power(b)] = cost(b);
// Now we build the array moving forward
for i from 0 to p:
// Start with a really big number
solution[i] = +Infinity;
// Iterate over base case to see what the cheapest way to get i power is.
for each base case b:
solution[i] = min(solution[i], solution[i - power(b)] + cost(b);
// The final answer is the last element in the array, but you get everything
// else for free. You can even work backwards and figure out the cheapest
// combination!
return solution[p]