简体   繁体   English

PHP - 困难的数学计算

[英]PHP - Difficult math calculation

Project description 项目描述

The project calculates how much food a horse should have, this is based on a huge number of variables. 该项目计算了马应该有多少食物,这是基于大量的变量。 The user enters information about each horse and preferred feeds. 用户输入有关每匹马和首选饲料的信息。 Each feed has multiple types of vitamins and nutrients. 每种饲料都含有多种维生素和营养素。

1 horse uses multiple types of feeds. 1匹马使用多种饲料。

What we have 我们有什么

We have the min and max values for each nutrient that the specific horse need. 我们有特定马需要的每种营养素的最小值和最大值。

We have the content for one or more different types of feed, this could be around 20-30 different nutrients and vitamins per feed. 我们有一种或多种不同类型饲料的含量,每种饲料可能含有约20-30种不同的营养素和维生素。 We also have the price and want to use this to save money. 我们也有价格,并希望用它来省钱。

(The calculation is only for one horse at the time) (计算仅适用于当时的一匹马)

Example

We use A, B and C to represent the nutrients. 我们用A,B和C来代表营养素。

Horse needs: (A,B,C) MIN(30,7,9) MAX(35,9,17) 马需要:(A,B,C)MIN(30,7,9)MAX(35,9,17)

Feed 1 contains: (A,B,C) VALUES(16,2,3) Feed 1包含:(A,B,C)VALUES(16,2,3)
Feed 2 contains: (A,B,C) VALUES(0,4,9) Feed 2包含:(A,B,C)VALUES(0,4,9)

A working solution would be 2*Feed1 and 1*Feed2. 一个有效的解决方案是2 * Feed1和1 * Feed2。

Problem 问题

I want the system to calculate the perfect balance based on the min/max values for each nutrient, and still keep the price as low as possible. 我希望系统根据每种营养素的最小/最大值计算完美平衡,并保持尽可能低的价格。

Working solution 工作方案

If I first calculate the highest possible amount for each feed, it will be possible to randomize until it seems to work. 如果我首先计算每个饲料的最高可能量,则可以随机化,直到它看起来有效。 And then the user will be able to change the amounts if it isn't perfect. 然后,如果用户不完美,用户将能够更改金额。

<?php
function randomizerLoop(){
    foreach($feeds as $feed){
        $max['A'] = floor($horse_max['A']/$feed['A']);
        $max['B'] = floor($horse_max['B']/$feed['B']);
        $max['C'] = floor($horse_max['C']/$feed['C']);
        $maxRand = MIN($max['A'], $max['B'], $max['C']);

        $amounts[$feed['id']] = rand(0, $maxRand);
    }

    return $amounts;
}
?>

This code would keep trying until it get a working balance, instead of using some cool calculation to find the balance on the first try. 此代码将继续尝试,直到它获得工作余额,而不是使用一些很酷的计算来找到第一次尝试的余额。

I just need an idea how to solve it without rand() . 我只是想知道如何在没有rand()情况下解决它。

More information 更多信息

Each user will be able to add endless number of horses, but will only be able to calculate for one horse at the time (for now). 每个用户将能够添加无数的马匹,但只能计算当时的一匹马(目前)。

It will also be possible to add endless number of feeds (defined by the user), and each feed could have 20-30 variables. 还可以添加无数个馈送(由用户定义),每个馈送可以有20-30个变量。 Depending on the solution, this would probably require a limit of feeds for each auto-calculation. 根据解决方案,这可能需要为每次自动计算限制Feed。

There would be a lot combinations if we use 20 different feeds, 20-30 variables for each feed, and also when we define amount in int instead of just booleans. 如果我们使用20个不同的提要,每个提要20-30个变量,以及我们在int中定义数量而不仅仅是布尔值时,会有很多组合。

This is a linear programming problem. 这是线性编程问题。

     MIN     MAX
A    30      35
B    7       9
C    9       17

          A    B    C
Feed1     16   2    3
Feed2     0    4    9

Let the food contain x number of feed1 and y number of feed2. 让食物包含x个feed1和y个feed2。 According to your question: 根据你的问题:

30<16*x+0*y<35
=> 30<16x<35

use ceil() to divide 30 by 16, that will give ux (which is 2). 使用ceil()将30除以16,这将给出ux(即2)。 After that you have 3<4y<5, similarly use ceil() and you will get y=1. 之后你有3 <4y <5,类似地使用ceil() ,你会得到y = 1。

Now thinking in terms of you project, 现在考虑你的项目,

For large number of feeds it will be difficult to calculate so many equations. 对于大量的饲料,很难计算出这么多的方程式。

We should use matrices for simplification.The martix for the equations given above will be :- 我们应该使用矩阵来简化。上面给出的方程式的martix将是: -

  A   *  B   =   C
|16 0| | x |   | 30 |
|2  4| | y | = |  7 |
|3  9|         |  9 |

Now use Lapack::pseudoInverse() to compute the inverse of matrix A and multiply it with C. Then simply equate the value of x and y in matrix B to the answer and use ceil(). 现在使用Lapack :: pseudoInverse()计算矩阵A的逆矩阵并将其乘以C.然后简单地将矩阵B中x和y的值等于答案并使用ceil()。

What you want to do is try every combination of every feed. 你想要做的是尝试每种饲料的每种组合。 Lets assume that there are 5 types of feeds. 让我们假设有5种类型的Feed。 You already know how to calculate the maximum of each feed type. 您已经知道如何计算每种Feed类型的最大值。 So, I assume you have something like this: 所以,我假设你有这样的事情:

$feeds_cost = array of costs for each feed
$feeds_ingredient_A = array of how much ingredient A each feed has
$feeds_ingredient_B = array of how much ingredient B each feed has
$feeds_ingredient_C = array of how much ingredient C each feed has
$feeds_max = array of maximum quantity of each feed allowed

Now, for the 5 types, you want to try quantities {0,0,0,0,0}, {0,0,0,0,1}, {0,0,0,0,2} ... {$feeds_max[0], $feeds_max[1], $feeds_max[2], $feeds_max[3], $feeds_max[4]}. 现在,对于5种类型,您想要尝试数量{0,0,0,0,0},{0,0,0,0,1},{0,0,0,0,2} ... {$ feeds_max [0],$ feeds_max [1],$ feeds_max [2],$ feeds_max [3],$ feeds_max [4]}。 For each quantity set, you need to: 1. Ensure that the quantity meets the minimum requirement. 对于每个数量集,您需要:1。确保数量满足最低要求。 2. If it does meet the minimum requirement, calculate the cost. 2.如果满足最低要求,则计算成本。

So, at this point, you have the cost for every quantity that meets the minimum requirement, but doesn't surpass the maximum requirement. 因此,此时,您需要满足最低要求的每个数量的成本,但不超过最大要求。 You don't need to store all of them. 您无需存储所有这些内容。 Maintain two variables: $best_quantities (an array of counts of each feed) and $best_cost. 保持两个变量:$ best_quantities(每个Feed的计数数组)和$ best_cost。 If a quantity has a cheaper cost while meeting the requirements, you replace $best_quantities and $best_cost. 如果数量在满足要求的同时成本更低,则替换$ best_quantities和$ best_cost。

Everything is trivial except stepping through all the quantities. 除了踩过所有数量之外,一切都是微不足道的。 That isn't really very hard. 这真的不是很难。 You maintain the index you are incrementing. 您维护正在递增的索引。 Initially, it is zero, but it goes up to the highest feed index (4 if you have 5 feeds). 最初,它为零,但它会达到最高的Feed索引(如果您有5个Feed,则为4)。 The increment function is: 增量函数是:

function increment_counts($feeds_max, $quantities, $index)
{
    if($index>sizeof($quantities)) return null;
    $quantities[$index]++;
    if($quantities[$index] > $feeds_max[$index])
    {
        $quantities[$index]=0;
        return increment_counts($feeds_max, $quantities, $index+1);
    }
    return $quantities;
}

Assuming that I typed that correctly off the top of my head, it will count through the quantities. 假设我正确地输入了我的头顶,它将计算数量。 You keep calling it by incrementing index 0 and replacing your current quantity with the return value. 您可以通过递增索引0并使用返回值替换当前数量来继续调用它。 When it returns null, you are done. 当它返回null时,你就完成了。

I am certain that I made at least one oversight, but this is how I would tackle this problem. 我确信我至少做过一次疏忽,但这就是我要解决这个问题的方法。

I would focus on the MIN values in order to minimise the cost. 我将重点关注MIN值,以便最大限度地降低成本。 In any case, the whole point of the suggested approach is meeting certain targets, which might be as variable as required (for example: forcing nutrients E & G to get, at least, the half value between MIN & MAX). 在任何情况下,建议方法的全部要点是满足某些目标,这些目标可能根据需要变化(例如:强制营养物E&G至少得到MIN和MAX之间的半值)。

The basic structure is formed by three loops: a main one going through all the nutrients; 基本结构由三个环形成:一个主要通过所有营养素; and two internal ones trying all the possible combinations among feeds. 和两个内部尝试饲料之间的所有可能组合。

NUTRIENT A
Trying feed1 until reaching minimum (because feed2 is zero). 
Best so far: 2*feed1=32A.

NUTRIENT B
Starting from 2*feed1=4B, +feed2 reaches minimum.
Best so far: 2*feed1+feed2=8B.

NUTRIENT C
Starting from 2*feed1+feed2=15C which is fine already.
Best so far: 2*feed1+feed2=15C.

A more advanced version of this approach would be going back to check (eg, in cases where the target is reached since a first moment, like what happens with nutrient C) whether a better alternative would be possible. 该方法的更高级版本将返回检查(例如,在从第一时刻到达目标的情况下,如营养物C发生的情况)是否可能有更好的替代方案。 In any case, the complexity of such an implementation and the number of combinations would be notably higher. 在任何情况下,这种实现的复杂性和组合的数量将显着更高。

I think that this is a fairly adaptable and accurate approach. 我认为这是一种相当适应和准确的方法。 Implementing the algorithm for the basic version is not too difficult (but neither too straightforward; that's why I haven't written it here) and should deliver a reasonably good accuracy and speed. 为基本版本实现算法并不太困难(但也不是太简单;这就是我没有在这里写的原因)并且应该提供相当好的准确性和速度。 After testing it and getting an idea about its performance, you might start thinking about improving it further (eg, via going back to re-analyse certain case or accounting for non-MIN targets). 在对其进行测试并了解其性能之后,您可能会开始考虑进一步改进它(例如,通过重新分析某些案例或计算非MIN目标)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM