Рассчитайте стоимость доставки по весу, используя массив многоуровневых цен

Мне нужно найти стоимость доставки коробки, основываясь на ряде правил веса, подобных этому:

$rules = [
    '1'     => '1.2',
    '5-10'  => '6.25',
    '10-15' => '9.2',
    '15-20' => '10.9',
];

Цель состоит в том, чтобы оптимизировать результат так, чтобы использовалось наименьшее количество правил с наименьшими возможными затратами.

Для коробки весом 47 фунтов эти наборы правил технически соответствуют весу:

  • (1)x47
  • (5-10)х5
  • (10-15)х3,(1)х2
  • (15-20)х3
  • (15-20)х2,(10-15)х1
  • (15-20)х2,(5-10)х1
  • и т. д.

Наилучший/желательный расчет цены будет составлять (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 раз — не самый экономичный путь.

В целом, к сожалению, мой подход во многом ошибочен. Может ли кто-нибудь помочь мне найти правильный код для решения этой проблемы?

Ваш массив «уровней» выглядит неправильно, у вас есть 1, 10, 15 и 20 - поскольку большинство этих значений являются верхней границей (10, 15, 20), первое, по моему мнению, должно быть 5, а не 1.

Nigel Ren 12.04.2024 07:20
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
4
1
115
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я считаю, что с непоследовательной структурой ваших весовых диапазонов неудобно работать, НО я точно понимаю, почему так должно быть - чтобы отдельные фунты суммировались правильно.

Я создал сценарий, который пытается найти самый дешевый квалификационный уровень, записать данные уровня, вычесть максимальное значение диапазона из входного веса, а затем повторить. Кажется, это работает для всех тестов, которые я создал.

Код: (Демо)

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 по ссылке, чтобы функция напрямую добавляла к нему. Не могли бы вы помочь мне с этим?

Faye D. 25.04.2024 03:51

Позвольте мне уточнить, что я имею в виду под «функция должна напрямую добавляться в массив правил». Таким образом, массив правил имеет в качестве значений не стоимость каждого диапазона, а сумматор «умножений». Итак, предположим, что он уже содержит некоторые диапазоны (ключи) и частоту этого диапазона, я бы хотел, чтобы функция добавляла частоту, рассчитанную для каждого из диапазонов, к существующим.

Faye D. 25.04.2024 04:05

Подожди секунду, ты почти обманул меня. Вы не автор этого вопроса. Если у вас есть новый вопрос, который связан с этой страницей, но не решен на ней, то... задайте новый вопрос, предложите ссылку на эту страницу и выделите/объясните ту часть, которую вы не можете решить. Я готов ответить на вопросы сегодня, потому что сегодня государственный праздник. Обязательно включите достаточно сложный минимально воспроизводимый пример.

mickmackusa 25.04.2024 05:19

К сожалению, сейчас не могу, так как не смогу еще какое-то время задавать вопросы... Но я нашла это сама. Не могли бы вы подтвердить мои изменения? Кажется, все работает нормально, но хотелось бы получить подтверждение. Если да, то я могу прислать вам минимальный воспроизводимый пример.

Faye D. 25.04.2024 05:27

Микмаккуса вот это onlinephp.io/c/f03c4

Faye D. 25.04.2024 05:34

@Фэй Тебе нужна вся эта свертка? Вам нужен первый элемент без ранжирования? Боюсь, существует слишком много бизнес-логики, которой я не знаю. Соответствует ли этот гораздо более простой сценарий всем вашим потребностям? 3v4l.org/dYoTS

mickmackusa 25.04.2024 07:03

mickmackusa, исходный массив $weight_distribution будет иметь ключи точно так же, как в моем MRE; отличаться будут только его значения. То есть будет ложный диапазон с ключом, равным 1, а затем законные диапазоны с минимальными-максимальными ограничениями.

Faye D. 25.04.2024 07:32

Я не могу придумать каких-либо значимых усовершенствований. 3v4l.org/dBsO8++ — это просто более простой способ записи += 1.

mickmackusa 25.04.2024 08:37

Другие вопросы по теме