admin管理员组文章数量:1122846
I'm trying to split items evenly through groups with a max on each group.
I have a function which receives a $groups
array which declares the maximum capacity of each "group" and an $items
integer which declares the total value which can be distributed across the elements of $groups
.
This is my function so far:
public static function splitItems($groups, $items) {
$n = count($groups);
$res = array_fill(0, $n, 0);
while ($items >= $groups[0]) {
for ($i = $n - 1; $i >= 0; $i--) {
if ($items >= $groups[$i]) {
$res[$i]++;
$items -= $groups[$i];
}
}
}
if ($items > 0) {
$res[0]++;
}
return $res;
}
My function isn't properly taking into account the VALUE of array as max value. What I'm looking for are this results:
Input:
splitItems([14, 2], 10);
splitItems([6, 8, 2], 14);
Output:
Array
(
[0] => 8
[1] => 2
)
Array
(
[0] => 6
[1] => 6
[2] => 2
)
I'm trying to split items evenly through groups with a max on each group.
I have a function which receives a $groups
array which declares the maximum capacity of each "group" and an $items
integer which declares the total value which can be distributed across the elements of $groups
.
This is my function so far:
public static function splitItems($groups, $items) {
$n = count($groups);
$res = array_fill(0, $n, 0);
while ($items >= $groups[0]) {
for ($i = $n - 1; $i >= 0; $i--) {
if ($items >= $groups[$i]) {
$res[$i]++;
$items -= $groups[$i];
}
}
}
if ($items > 0) {
$res[0]++;
}
return $res;
}
My function isn't properly taking into account the VALUE of array as max value. What I'm looking for are this results:
Input:
splitItems([14, 2], 10);
splitItems([6, 8, 2], 14);
Output:
Array
(
[0] => 8
[1] => 2
)
Array
(
[0] => 6
[1] => 6
[2] => 2
)
Share
Improve this question
edited Nov 25, 2024 at 20:50
mickmackusa
47.7k13 gold badges92 silver badges159 bronze badges
Recognized by PHP Collective
asked Nov 22, 2024 at 14:02
EredEred
4971 gold badge6 silver badges24 bronze badges
5
|
2 Answers
Reset to default 2- To target smaller limits first, call
asort()
to order your elements ascending and preserve the original indexes. - Then loop over the elements and update their value to be the lesser amount of their original value and the amount remaining in the total.
- Then reduce the total by the amount applied to the element and repeat.
- Before returning the updated elements, return them to their original order, if it matters.
This effectively fills the smaller capacities before larger capacities and never overfills. Demo
function splitItems(array $limits, int $total): array {
asort($limits);
foreach ($limits as &$limit) {
$limit = min($limit, $total);
$total -= $limit;
}
ksort($limits);
return $limits;
}
var_export(splitItems([14, 2], 10));
echo "\n---\n";
var_export(splitItems([6, 8, 2], 14));
echo "\n---\n";
var_export(splitItems([6, 4, 2], 14));
echo "\n---\n";
var_export(splitItems([7, 7, 1], 4));
echo "\n---\n";
var_export(splitItems([1, 1], 10));
echo "\n---\n";
var_export(splitItems([2,1,3],4));
Output:
array (
0 => 8,
1 => 2,
)
---
array (
0 => 6,
1 => 6,
2 => 2,
)
---
array (
0 => 6,
1 => 4,
2 => 2,
)
---
array (
0 => 3,
1 => 0,
2 => 1,
)
---
array (
0 => 1,
1 => 1,
)
---
array (
0 => 2,
1 => 1,
2 => 1,
)
p.s. If you don't care about filling the smallest capacity first, or if filling the items in order of their initial position is desired, just remove the two *sort()
calls.
Update: For balanced distribution, you can loop over the containers one-at-a-time until they are full or the total is entirely consumed. Demo
function splitItems(array $limits, int $total): array {
$i = 0;
$count = count($limits);
$result = array_fill(0, $count, 0); // set 0 defaults for all containers
do {
if ($result[$i] < $limits[$i]) { // this container still has room
++$result[$i]; // add 1 to container
--$total; // subtract 1 from total
}
$i = ($i + 1) % $count; // update $i with circular access to allow restarting from 0
} while ($total && $result !== $limits); // loop until $total is emptied or all limits are filled
return $result;
}
This should work:
function splitItems($groups, $items)
{
$n = count($groups);
$res = array_fill(0, $n, 0);
$groupsFilled = array_fill(0, $n, 0);;
while ($items > 0 && count(array_keys($groupsFilled, 1)) < count($groupsFilled))
{
for ($i = $n-1; $i >= 0; $i--)
{
if ($res[$i] < $groups[$i])
{
if ($items > 0) {
$res[$i]++;
$items--;
}
}
else $groupsFilled[$i] = 1;
}
}
return $res;
}
print_r(splitItems([14,2],10));
print_r(splitItems([6,8,2],14));
print_r(splitItems([6,4,2],14)); //not enough space for all the items!
Outputs:
Array
(
[0] => 8
[1] => 2
)
Array
(
[0] => 6
[1] => 6
[2] => 2
)
Array
(
[0] => 6
[1] => 4
[2] => 2
)
Key points:
- The code loops until there are no more items left to distribute (in most cases, but see the last bullet point below).
- As per your original logic it will put an item the last group first, and then work backwards to the first one. It distributes one item to each group, then goes round again until there's nothing left.
- It checks to ensure the group is not full before putting an item into it.
- If you specify less total space in the groups than there are items, it will just fill the groups until there is no space left in any of them - that's what the second condition in the
while
loop is doing. (What it doesn't do is tell you how many items couldn't be put into a group - but you could alter that if you wanted to.)
Live demo: https://3v4l.org/R8BOg
本文标签:
版权声明:本文标题:php - Distribute total amount to a flat array of "container" elements with predefined limits - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1736303267a1931826.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
the array keys are the max value for each group
...it's unclear what this means in relation to the sample data supplied. In your example([14,2],10)
the array keys of the supplied array will be0
and1
. But the values in your output groups are8
and2
. Did you mean to say the array values (i.e.14
and2
in that example)? Make sure you get the terminology right, otherwise the question can be come ambiguous or confusing. – ADyson Commented Nov 22, 2024 at 14:11splitItems([1,1],10); the result is correct
. But also, is the rule meant to be that the smallest capacities get satisfied first? Are the distributions generally meant to be balanced-ish? Is it intended that the largest capacity been given the fewest items when there are insufficient items? Do we have enough challenging test cases to fully interpret and test the requirements? – mickmackusa Commented Nov 23, 2024 at 0:12