简体   繁体   中英

How to calculate parts of whole knowing max limit of each? (preferably php)

I have an algorithm which calculates the % of each entity (3 in total) from whole. But I also have a max % for each part which should not be exceeded. How to add this part to the algorithm ?

Example for total of 13% (there are other 87% reserved for other things):

x | calculated %  |  max %  |  should get
x1        2            20          6*
x2        5            4           4
x3        6            3           3

this kind of simple, but what if only one goes down and other two should share the rest according to their parts

x | calculated %  |  max %  |  should get
x1        2            10          2.5*
x2        5            10          6.5*
x3        6            4           4

thank you for help and advises

Here's one possible approach (tested in php 5.6) - it works by finding any entities that are already at or above maximum, moving them to a separate array, rebalancing the remaining entities by ratio, and then repeating to see if any more are at or above max. I've also added a function to round to the nearest 0.5 as this seems to be what you did in the example, although this means that the returned entities might not precisely add up to the total under certain circumstances.

<?php

function adjustEntities($total, array $entities)
{
    $entities_locked = array();
    while (true) {
        $found_any_at_max = false;
        $total_to_adjust = 0;
        foreach ($entities as $entity => $values) {
            if ($values['calculated'] >= $values['max']) {
                $found_any_at_max = true;
                $entities_locked[$entity] = $values['max'];
                $total -= $values['max'];
                unset($entities[$entity]);
            } else {
                $total_to_adjust += $values['calculated'];
            }
        }
        if (!$found_any_at_max || empty($entities)) {
            foreach ($entities as $entity => $values) {
                $entities_locked[$entity] = $values['calculated'];
            }
            ksort($entities_locked);
            return $entities_locked;
        }
        $ratio = $total / $total_to_adjust;
        foreach ($entities as $entity => $values) {
            $values['calculated'] = roundToNearestHalf($values['calculated'] * $ratio);
            $entities[$entity] = $values;
        }
    }
}

function roundToNearestHalf($value)
{
    return 0.5 * round(2 * $value);
}

Tested as follows:

<?php

$total = 13;

$entities1 = array(
    'x1' => array (
        'calculated' => 2,
        'max' => 20,
    ),
    'x2' => array (
        'calculated' => 5,
        'max' => 4,
    ),
    'x3' => array (
        'calculated' => 6,
        'max' => 3,
    ),
);

$entities2 = array(
    'x1' => array (
        'calculated' => 2,
        'max' => 10,
    ),
    'x2' => array (
        'calculated' => 5,
        'max' => 10,
    ),
    'x3' => array (
        'calculated' => 6,
        'max' => 4,
    ),
);

var_dump(adjustEntities($total, $entities1));
var_dump(adjustEntities($total, $entities2));

Gives output:

array(3) {
  ["x1"]=>
  float(6)
  ["x2"]=>
  int(4)
  ["x3"]=>
  int(3)
}
array(3) {
  ["x1"]=>
  float(2.5)
  ["x2"]=>
  float(6.5)
  ["x3"]=>
  int(4)
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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