2016-05-23 3 views
4

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

Задача: У меня есть 10 предметов. Если вы покупаете 1, это 10 долларов. Я продаю вам второй за 9 долларов. Я продаю вам третий товар за $ 8. Я буду продолжать снимать деньги, пока мы не доберемся до $ 5/item, потому что это самый низкий, на который я его продам. Итак, если вы купите все 10, это обойдется вам в 65 долларов.

Это модель ценообразования, которую я пытаюсь достичь, за исключением значительно большего масштаба. Вместо кучи предметов, использующих доллары, я говорю о миллионах и использую доли монет.

Это мой текущий код:

<?php 

function getCost($num_items) 
{ 
    $min_price   = 0.002; 
    $max_price   = 0.007; 
    $discount_range  = 1000000; 

    $discount_per_additional_item = ($max_price - $min_price)/($discount_range - 1); 

    $price_per_unit = MAX($min_price, ($max_price - ($num_items - 1) * $discount_per_additional_item)); 

    return $price_per_unit; 
} 

$array = [100, 1000, 10000, 100000, 200000, 300000, 400000, 500000, 600000, 700000, 800000, 900000, 1000000]; 

foreach ($array as $value) 
{ 

    $sum = 0; 
    for ($i = 0; $i < $value; ++$i) 
     $sum += getCost($i); 

    echo number_format($value) . ' | $' . number_format($sum) . "\n"; 

} 

Какие результаты в:

100 | $1 
1,000 | $7 
10,000 | $70 
100,000 | $675 
200,000 | $1,300 
300,000 | $1,875 
400,000 | $2,400 
500,000 | $2,875 
600,000 | $3,300 
700,000 | $3,675 
800,000 | $4,000 
900,000 | $4,275 
1,000,000 | $4,500 

Я использую $ массив в качестве проверки вменяемости, где в реальном мире, я бы просто вычислить для фактический номер, на который взимается клиент.

Мой вопрос: есть ли способ сделать это без использования цикла for? Что-то, может быть, более элегантно?

Я сделал пример онлайн: http://sandbox.onlinephpfunctions.com/code/47e270dbad8cbe16c9ea906ffd2dce098a52fbca

+1

Что Вам не нравится в цикле for? Я не вижу в этом ничего плохого. – GrumpyCrouton

+0

Вопрос: какова цель '$ sum' в вашем коде? почему результат '100' имеет значение« 1000 »? Если вы продаете игрушки '100', тогда этого значения должно быть достаточно, чтобы найти скидку на' 100', а если вы продаете игрушки '100 000', то вам не нужно знать цену, по которой вы продали' 100', игрушек, чтобы рассчитать определенную цену игрушек «100 000». – Webeng

+1

@GrumpyCrouton мне кажется, что OP связан с количеством циклов цикла, выполняемых на более высоких числах. –

ответ

4

Этот код будет иметь тот же результат, и не имеет внутренний цикл:

$min_price   = 0.002; 
$max_price   = 0.007; 
$discount_range  = 1000000; 
$discount_per_additional_item = ($max_price - $min_price)/($discount_range - 1); 

$num_progressively_discounted_items = 
     ceil(($max_price - $min_price)/$discount_per_additional_item); 
foreach ($array as $value) { 
    $num_items_above_min = min($value, $num_progressively_discounted_items); 
    $num_items_at_min = $value - $num_items_above_min; 
    $sum = $num_items_at_min * $min_price + 
      $num_items_above_min * $max_price - 
      $discount_per_additional_item 
       * $num_items_above_min * ($num_items_above_min - 1)/2; 

    echo number_format($value) . ' | $' . number_format($sum) . "\n"; 
} 

Это то, что он делает:

  • Сначала он проверяет, сколько раз скидка на единицу может быть вычтена из первоначальной цены до достижения минимальной цены. Если больше, чем количество предметов, которые вы покупаете, то эта рассчитанная цифра корректируется на это количество элементов.
  • Остальное количество предметов (если есть) также принимаются к сведению: все они имеют минимальную цену.
  • Сумма состоит из двух частей. Легкая часть представлена ​​количеством предметов, которые будут идти за минимальной ценой, и это простое умножение.
  • Вторая часть суммы состоит из всегда убывающего срока или иным образом ставится: это максимальная цена за количество предметов, которые не идут на минимальную цену, минус сумма 0+1+2+3+4+5...+n. Для этого известна формула: n(n-1)/2.

Как я уже говорил в комментариях, есть что-то странное в вашем коде: для $i=0 значение, возвращаемое getCost($i) выше, чем максимальная цена, так как блок скидка добавляется к нему. Это можно исправить, запустив внутренний цикл с $i=1. Во всяком случае, это означает, что в результате моего предлагаемого кода есть небольшая разница, так как у него нет этой особенности. Но поскольку скидка на единицу настолько мала, вы на самом деле не замечаете ее в печатном виде.

+0

Мне это нравится, но если '$ discount_range' всегда 1,000,000, тогда было бы целесообразно статически назначать 999,999 вместо вычисления' ($ discount_range - 1) 'миллиона раз? Я лично все еще пытаюсь расшифровать цели OPs, поэтому вполне возможно, что я ничего не понимаю. Фактически, '$ discount_per_additional_item' может быть статически назначено. – MonkeyZeus

+0

Действительно, в этом ответе я предположил, что для одной партии элементов дисконт единицы является постоянным, что имеет место в коде OP. Обратите внимание, что по этой причине я вывел вычисление '$ discount_per_additional_item' из цикла: он всегда дает тот же результат. – trincot

+0

Спасибо. Это намного быстрее. – S16

0

Вы можете сделать это немного больше функциональный стиль:

function sumOfNaturalSeries($n) 
{ 
    return ((1 + $n)/2) * $n; 
} 

$minPrice = 0.002; 
$maxPrice = 0.007; 
$discountRange = 1000000; 

$discountStep = ($maxPrice - $minPrice)/$discountRange; 

$getPrice = function ($numberOfItems) use (
    $minPrice, 
    $maxPrice, 
    $discountRange, 

    $discountStep 
) { 
    if ($numberOfItems <= $discountRange) { 
     return $maxPrice * $numberOfItems - sumOfNaturalSeries($numberOfItems - 1) * $discountStep; 
    } 

    $itemsAboveRange = $numberOfItems - $discountRange; 

    return $maxPrice * $discountRange - sumOfNaturalSeries($discountRange - 1) * $discountStep + $minPrice * $itemsAboveRange; 
}; 

$array = [100, 1000, 10000, 100000, 200000, 300000, 400000, 500000, 600000, 700000, 800000, 900000, 1000000]; 

$sums = array_map($getPrice, $array); 

var_dump($sums); 
var_dump(array_map('number_format', $sums)); 

Вот demo.

Обратите внимание на вычислительную ошибку.

Смежные вопросы