Мне нужно найти стоимость доставки коробки, основываясь на ряде правил веса, подобных этому:
$rules = [
'1' => '1.2',
'5-10' => '6.25',
'10-15' => '9.2',
'15-20' => '10.9',
];
Цель состоит в том, чтобы оптимизировать результат так, чтобы использовалось наименьшее количество правил с наименьшими возможными затратами.
Для коробки весом 47 фунтов эти наборы правил технически соответствуют весу:
Наилучший/желательный расчет цены будет составлять (15-20)
10,90 долларов США + (15-20)
10,90 долларов США + (5-10)
6,25 долларов США = 28,05 долларов США за доставку. Это сумма самой дешевой комбинации цен.
Сначала, для простоты, я создал промежуточный массив (верхних пределов каждого правила диапазонов) следующим образом (1 не имеет верхнего/нижнего предела, но его легко исключить):
$tiers = [
20,
15,
10,
1,
];
А затем я попытался распределить начальный вес по этому массиву аналогично процессу факторизации. Поэтому сначала я вообще проигнорировал нижние пределы и взял вес, т.е. 37,75 фунтов. Затем, используя следующий код, я создал массив «факторизации» каждого уровня веса:
print_r( distribute( 37.75 );
function distribute( $weight = 0 ) {
$tiers = [1, 10, 15, 20];
rsort( $tiers );
foreach ( $tiers as $tier ) {
$counters[$tier] = 0;
}
foreach ( $tiers as $tier ) {
$quotient = $weight / $tier;
$floored = floor( $quotient );
$remaining = $weight - $floored * $tier;
if ( $quotient >= 1 && $remaining > 1 ) {
$counters[$tier] = $floored;
$weight = $remaining;
} else if ( $tier == 1 ) {
$counters[$tier] = ( $floored + 1 );
$weight = $weight - ( $floored + 1 ) * $tier;
}
}
return $counters;
}
Что удобно дало такой результат:
Array (
[20] => 1
[15] => 1
[10] => 0
[1] => 3
)
Затем я попробовал тот же код с весом 38 и осознал свою первую ошибку... Есть некоторая проблема с крайним случаем, которую я пока не могу понять: для 38 все еще добавляется +1
к правилу 1 уровня.
Затем я попробовал 47,75 фунтов и обнаружил вторую ошибку... Как я уже сказал, для простоты я использовал верхние пределы, что мешает «факторизации» веса. Итак, для веса 47,75 фунтов приведенный выше код выдал такой результат:
Array (
[20] => 2
[15] => 0
[10] => 0
[1] => 8
)
что совершенно неверно, поскольку потребление 1-го уровня 8 раз — не самый экономичный путь.
В целом, к сожалению, мой подход во многом ошибочен. Может ли кто-нибудь помочь мне найти правильный код для решения этой проблемы?
Я считаю, что с непоследовательной структурой ваших весовых диапазонов неудобно работать, НО я точно понимаю, почему так должно быть - чтобы отдельные фунты суммировались правильно.
Я создал сценарий, который пытается найти самый дешевый квалификационный уровень, записать данные уровня, вычесть максимальное значение диапазона из входного веса, а затем повторить. Кажется, это работает для всех тестов, которые я создал.
Код: (Демо)
function costFromWeight($weight, array $newRules): array {
$parsed = [];
foreach ($newRules as $range => $cost) {
if (sscanf($range, '%d-%d', $min, $max) === 1) {
$default = [$min, $min, (float) $cost];
} else {
$parsed[] = [$min, $max, (float) $cost];
}
}
$result = ['cost' => 0];
while ($weight > 0) {
foreach ($parsed as $i => [$min, $max, $cost]) {
if ($weight <= $max) {
if (!$i && $weight <= $min) { // weight is lower than lowest range, use defaults
[$min, $max, $cost] = $default;
}
break;
}
}
$result[$max] = ($result[$max] ?? 0) + 1;
$result['cost'] += $cost;
$weight -= $max;
}
return $result;
}
$rules = [
'1' => '1.2',
'5-10' => '6.25',
'10-15' => '9.2',
'15-20' => '10.9',
];
foreach ([49, 47.75, 38, 35, 23, 15, 14, 10.5, 4] as $weight) {
echo "$weight :: ";
print_r(costFromWeight($weight, $rules));
echo "\n---\n";
}
Выход:
49 :: Array
(
[cost] => 28.05
[20] => 2
[10] => 1
)
---
47.75 :: Array
(
[cost] => 28.05
[20] => 2
[10] => 1
)
---
38 :: Array
(
[cost] => 21.8
[20] => 2
)
---
35 :: Array
(
[cost] => 20.1
[20] => 1
[15] => 1
)
---
23 :: Array
(
[cost] => 14.5
[20] => 1
[1] => 3
)
---
15 :: Array
(
[cost] => 9.2
[15] => 1
)
---
14 :: Array
(
[cost] => 9.2
[15] => 1
)
---
10.5 :: Array
(
[cost] => 9.2
[15] => 1
)
---
4 :: Array
(
[cost] => 4.8
[1] => 4
)
---
Mickmackusa, это почти то, что мне тоже нужно, за исключением того, что ключ 'cost'
не важен для моего случая, вместо этого я хотел бы передать массив $newRules
по ссылке, чтобы функция напрямую добавляла к нему. Не могли бы вы помочь мне с этим?
Позвольте мне уточнить, что я имею в виду под «функция должна напрямую добавляться в массив правил». Таким образом, массив правил имеет в качестве значений не стоимость каждого диапазона, а сумматор «умножений». Итак, предположим, что он уже содержит некоторые диапазоны (ключи) и частоту этого диапазона, я бы хотел, чтобы функция добавляла частоту, рассчитанную для каждого из диапазонов, к существующим.
Подожди секунду, ты почти обманул меня. Вы не автор этого вопроса. Если у вас есть новый вопрос, который связан с этой страницей, но не решен на ней, то... задайте новый вопрос, предложите ссылку на эту страницу и выделите/объясните ту часть, которую вы не можете решить. Я готов ответить на вопросы сегодня, потому что сегодня государственный праздник. Обязательно включите достаточно сложный минимально воспроизводимый пример.
К сожалению, сейчас не могу, так как не смогу еще какое-то время задавать вопросы... Но я нашла это сама. Не могли бы вы подтвердить мои изменения? Кажется, все работает нормально, но хотелось бы получить подтверждение. Если да, то я могу прислать вам минимальный воспроизводимый пример.
Микмаккуса вот это onlinephp.io/c/f03c4
@Фэй Тебе нужна вся эта свертка? Вам нужен первый элемент без ранжирования? Боюсь, существует слишком много бизнес-логики, которой я не знаю. Соответствует ли этот гораздо более простой сценарий всем вашим потребностям? 3v4l.org/dYoTS
mickmackusa, исходный массив $weight_distribution
будет иметь ключи точно так же, как в моем MRE; отличаться будут только его значения. То есть будет ложный диапазон с ключом, равным 1, а затем законные диапазоны с минимальными-максимальными ограничениями.
Я не могу придумать каких-либо значимых усовершенствований. 3v4l.org/dBsO8++
— это просто более простой способ записи += 1
.
Ваш массив «уровней» выглядит неправильно, у вас есть 1, 10, 15 и 20 - поскольку большинство этих значений являются верхней границей (10, 15, 20), первое, по моему мнению, должно быть 5, а не 1.