Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHP - Difficult math calculation

Tags:

php

math

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.

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. 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.

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

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

A working solution would be 2*Feed1 and 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().

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. Depending on the solution, this would probably require a limit of feeds for each auto-calculation.

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.

like image 959
SebHallin Avatar asked Sep 01 '15 17:09

SebHallin


2 Answers

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. According to your question:

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

use ceil() to divide 30 by 16, that will give u x (which is 2). After that you have 3<4y<5, similarly use ceil() and you will get 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 :-

  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().

like image 135
Golden_flash Avatar answered Nov 02 '22 02:11

Golden_flash


What you want to do is try every combination of every feed. Lets assume that there are 5 types of feeds. You already know how to calculate the maximum of each feed type. 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]}. For each quantity set, you need to: 1. Ensure that the quantity meets the minimum requirement. 2. If it does meet the minimum requirement, calculate the cost.

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. If a quantity has a cheaper cost while meeting the requirements, you replace $best_quantities and $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). 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. When it returns null, you are done.

I am certain that I made at least one oversight, but this is how I would tackle this problem.

like image 4
kainaw Avatar answered Nov 02 '22 04:11

kainaw