Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/php/251.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Php 计算哪些产品一起可以提供所需的功率_Php_Algorithm_Optimization - Fatal编程技术网

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]