2016-11-13 102 views
1

我正在開發的程序的一部分實現了將包裹重量作爲參數並根據該重量計算運費的功能。對成本/磅的標準如下:C++根據重量計算運費成本

 Package Weight    Cost 
     --------------    ---- 
     25 lbs & under    $5.00 (flat rate) 
     26 - 50 lbs     above rate + 0.10/lb over 25 
     50 + lbs     above rate + 0.07/lb over 50 

我用了一個if的if else,如果做出的計算,但感覺它有點重複:

const int TIER_2_WEIGHT = 25; 
const int TIER_3_WEIGHT = 50; 

const float TIER_1_RATE = 5.00; 
const float TIER_2_RATE = 0.10; 
const float TIER_3_RATE = 0.07; 

float shipPriceF; 


if(shipWeightF <= TIER_2_WEIGHT) 
{ 
    shipPriceF = TIER_1_RATE; 
} 
else if(shipWeightF <= TIER_3_WEIGHT) 
{ 
    shipPriceF = ((shipWeightF - TIER_2_WEIGHT) * TIER_2_RATE) + 
        TIER_1_RATE; 
} 
else 
{ 
    shipPriceF = ((shipWeightF - TIER_3_WEIGHT) * TIER_3_RATE) + 
       ((TIER_3_WEIGHT - TIER_2_WEIGHT) * TIER_2_RATE) + 
        TIER_1_RATE; 
} 

return shipPriceF; 

因此,問題是......這是完成這項任務的最佳方式,還是我應該尋找不同的解決方案?

+3

你有什麼是完美的做法完美無暇。就使用if-else if-else而言,沒有任何重複。 – VHS

+0

此外,這裏沒有任何遞歸。 –

+0

假設這些是唯一的層次,這看起來很好。 – Qix

回答

2

首先,你的代碼看起來很清楚,確實如此。

當然,你可以通過使用累計的方式進行重複數據刪除公式的多餘部分:

float shipPriceF = TIER_1_RATE; // to be paid anyway 

if (shipWeightF > TIER_2_WEIGHT) // add the tier 2 if necessary 
{ 
    shipPriceF += (min(shipWeightF, TIER_3_WEIGHT) - TIER_2_WEIGHT) * TIER_2_RATE; 
} 
if(shipWeightF > TIER_3_WEIGHT) // add the tier 3 if really necessary 
{ 
    shipPriceF += (shipWeightF - TIER_3_WEIGHT) * TIER_3_RATE); 
} 

那麼,這甚至可以進一步簡化:

float shipPriceF = TIER_1_RATE 
        + max(min(shipWeightF,TIER_3_WEIGHT)-TIER_2_WEIGHT,0) * TIER_2_RATE 
        + max(shipWeightF-TIER_3_WEIGHT,0) * TIER_3_RATE; 

對於3個尺度,它的大概可以用這個合成公式。如果你想要更多的靈活性,你可以考慮通過速率向量迭代,而不是使用常量。這將允許可變數量的比例。如果你確定公式總是漸進式的(例如,「高於+新單價」),則使用累積方法。

+0

感謝您的反饋!這更多的是我心中的想法,但無法想象。非常感激! – jslice

+0

雖然簡化了您的解決方案,但效率會有什麼折衷嗎?第一個使用的是比原來少一個的比較操作,但是最後一個使用的是最小/最大值。如果費率變化的規模更大,它是否會有所作爲? – jslice

+0

你確實是對的,但它很大程度上取決於優化器。例如,對於GCC 6.2,我的[第一個提議](https://godbolt.org/g/pPQCgO)只是一個比你的[原始代碼](https://godbolt.org/g/sbkze7)少的asm指令。而我的[ultraslim功能](https://godbolt.org/g/x3wxeA)更多是4條指令。但總體執行性能不僅取決於指令數量,還取決於重量的統計分佈(需要更多或更少的跳轉)。因此,我的承諾:「不成熟的優化是萬惡之源」。 – Christophe

0

我認爲代碼中有很多幾乎相同的行,但不是真正的重複。如果添加更多費率,您可以輕鬆複製錯誤的宏定義或混合錯誤率的值。

我的代碼本身刪除了if/else複製,並避免使用正確的全局定義。如果您爲我的代碼添加新的費率,只需將一個原始數據添加到表格中即可。

只給一個想法,還有什麼可以做:

#include <iostream> 
#include <functional> 
#include <limits> 

// first we define a entry of a table. This table contains the limit to which the ratio is valid and 
// a function which calculates the price for that part of the weight. 
struct RateTableEntry 
{ 
    double max; 
    std::function<double(double, double)> func; 
}; 

// only to shrink the table width :-) 
constexpr double MAX = std::numeric_limits<double>::max(); 

// and we define a table with the limits and the functions which calculates the price 
RateTableEntry table[]= 
{ 
    // first is flate rate up to 25 
    { 25, [](double , double  )->double{ double ret=      5.00; return ret; }}, 
    // next we have up to 50 the rate of 0.10 (use min to get only the weight up to next limit 
    { 50, [](double max, double weight)->double{ double ret= std::min(weight,max)*0.10; return ret; }}, 
    // the same for next ratio. std::min not used, bedause it is the last entry 
    { MAX, [](double , double weight)->double{ double ret=   weight  *0.07; return ret; }} 
}; 

double CalcRate(double weight) 
{ 
    std::cout << "Price for " << weight; 
    double price = 0; 
    double offset = 0; 
    for (auto& step: table) 
    { 
     // call each step, until there is no weight which must be calculated 
     price+=step.func(step.max- offset, weight); 
     // reduce the weight for that amount which allready is charged for 
     weight-=step.max-offset; 
     // make the table more readable, if not used this way, we have no max values but amount per step value 
     offset+=step.max; 
     if (weight <= 0) break; // stop if all the weight was paid for 
    } 

    std::cout << " is " << price << std::endl; 

    return price; 
} 

int main() 
{ 
    CalcRate(10); 
    CalcRate(26); 
    CalcRate(50); 
    CalcRate(51); 
    CalcRate(52); 
    CalcRate(53); 
} 

如果C++ 11不可用,你也可以用正常的函數和函數指針,而不是lambda表達式和std ::功能。