将数字拆分为可变费率结算的频段,即“分层定价”

时间:2017-09-26 13:16:00

标签: python algorithm billing

我有一个问题,在纸上很简单,但我很难在代码中解决问题。在进一步讨论之前,这不是字符串拆分问题

我有一个基本上是带状时间表的应用程序。比例是根据客户变化的,但一个例子是一个月的前三个小时是80,接下来的三个小时是70,其余的是50.我目前在代码中代表这个:

scale = [80, 80, 80, 70, 70, 70, 50]

......但我也对那里的建议持开放态度。

步骤的规模和数量是​​ - 并且必须是可变的。我的一些账单对某些客户来说要简单得多,但我希望能够提供这种高使用率的计划。

但是我如何花费数小时工作(例如15.2)并计算他们应该支付多少钱?如何将这个大数字分成乐队?正如我所说的那样,在纸上工作很容易,但随着我获得更多客户和更复杂的计划,这变得非常无聊。以下是我将如何处理15.2小时:

3 hours at 80 = 240
3 hours at 70 = 210
9.2 hours at 50 = 460
total = 910

虽然我很喜欢,但我很欣赏有关我想要描述的正确名称的评论。而Oli,如果你在2023年回到这里,下次选择一个更容易的计费方案,伙计。

4 个答案:

答案 0 :(得分:2)

如果我在这里正确理解问题,那就是我的尝试。它不紧凑,但我更喜欢可读。我将波段表示更改为[(hours, price), (hours, price), ... (None, Price)],请参见下面的示例:

def calculate_cost(hours, bands):
    remaining_hours = hours
    current_band_idx = 0
    total_cost = 0

    while remaining_hours > 0:
        band_max_hours, band_cost = bands[current_band_idx]

        if band_max_hours is None:
            band_billable_hours = remaining_hours
        else:
            band_billable_hours = min(remaining_hours, band_max_hours)

        total_cost += band_billable_hours * band_cost
        current_band_idx += 1
        remaining_hours -= band_billable_hours

    return total_cost

以你的例子为例:

>>> calculate_cost(15.2, [(3, 80), (3, 70), (None, 50)])
910.0

答案 1 :(得分:2)

首先,我会转过身来:

scale = [80, 80, 80, 70, 70, 70, 50]

进入这个:

import math

scale = {(0, 3): 80, (3, 6): 70, (6, math.inf): 50}

然后我的算法的其余部分如下:

# Total hours worked
hours_worked = 15.2

# Handle the decimal (if any) to begin with... First find the "max" rate
decimal_rate = next(rate for (lower, upper), rate in scale.items()
                        if lower <= hours_worked and upper >= hours_worked)

# Then calculate the last "sliver" of pay
decimal_end = hours_worked - int(hours_worked)
end_pay = decimal_end * decimal_rate

# Use an integer for ease of calculation
hours_worked = int(hours_worked)

hours_paid_for = 0

# Beginning total pay is just the decimal "ending"
total_pay = end_pay

while hours_paid_for < hours_worked:
    # Find the rate for the current bucket of hours
    rate_filter = (rate for (lower, upper), rate in scale.items() if lower <= hours_paid_for and hours_paid_for < upper)
    current_level = next(rate_filter)

    print('Hour: {}'.format(hours_paid_for))
    print('Pay rate: ${}'.format(current_level))

    total_pay += current_level

    hours_paid_for += 1

print('Total earned: ${}'.format(total_pay))

输出如下:

Hour: 0
Pay rate: $80
Hour: 1
Pay rate: $80
Hour: 2
Pay rate: $80
Hour: 3
Pay rate: $70
Hour: 4
Pay rate: $70
Hour: 5
Pay rate: $70
Hour: 6
Pay rate: $50
Hour: 7
Pay rate: $50
Hour: 8
Pay rate: $50
Hour: 9
Pay rate: $50
Hour: 10
Pay rate: $50
Hour: 11
Pay rate: $50
Hour: 12
Pay rate: $50
Hour: 13
Pay rate: $50
Hour: 14
Pay rate: $50
Total earned: $910.0

这里也是一个很好的功能:

def calculate_pay(scale, hours_worked):

    # Handle the decimal (if any) to begin with... First find the "max" rate
    decimal_rate = next(rate for (lower, upper), rate in scale.items()
                            if lower <= hours_worked and upper >= hours_worked)

    # Then calculate the last "sliver" of pay
    decimal_end = hours_worked - int(hours_worked)
    end_pay = decimal_end * decimal_rate

    # Use an integer for ease of calculation
    hours_worked = int(hours_worked)

    # Hours already paid for (int)
    hours_paid_for = 0

    # Beginning 'total pay' can be the decimal end, if any
    total_pay = end_pay

    while hours_paid_for < hours_worked:
        # Find the rate for the current bucket of hours
        rate_filter = (rate for (lower, upper), rate in scale.items()
                        if lower <= hours_paid_for and hours_paid_for < upper)
        current_level = next(rate_filter)

        total_pay += current_level

        hours_paid_for += 1

    return total_pay

答案 2 :(得分:1)

虽然在我的评论中我建议采用最简单/最天真的方法并且只是循环 - 我的好奇心让我变得更好,所以这里是使用递归的“少线”方法。

从一个“成本桶”列表开始,每个成本桶包含一定数量小时的价格(例如,[3,80] => 3小时,价格为80个单位),您可以将您的小时数分解为您已按顺序填充每个桶(或已到达最后一个桶,其中所有剩余时间都“花费”)。

# each bucket is a pair of [max hours at this rate, rate per hour]
def calc_cost(cost_buckets: list, hours_worked):
    if hours_worked <= cost_buckets[0][0] or len(cost_buckets) < 2:
        return cost_buckets[0][1] * hours_worked
    return calc_cost(cost_buckets, cost_buckets[0][0]) + calc_cost(cost_buckets[1:], hours_worked - cost_buckets[0][0])


hours = 2.5 # number of hours total worked
costs = [[1,12], [5,30]] # list of 'cost buckets'
print(calc_cost(costs, hours)) # prints '57.0'

这是一种不灵活的方法,但它会完全按照你的具体做法。

(我应该明确指出递归的常见问题;因为如果你想要有一个100,000工时计划,有20,000个成本桶 - 你可能不想这样做。)

答案 3 :(得分:0)

这是另一种方法,使用您首先使用的速率存储方案。 此方法假定项目的工作小时数至少与s中的费率数量相同(即len(s)

# Original rate scaling method
s = [80, 80, 80, 70, 70, 70, 50]

hours_worked = 15.2

final_hours_rate = s[-1]

# Handle the "decimal end"
end_pay = (hours_worked - int(hours_worked)) * final_hours_rate

hours_worked = int(hours_worked)

n_rates = len(s)

remainder = sum(s[hour] for hour in range(n_rates)) + \
            final_hours_rate * (hours_worked - n_rates)

total_pay = end_pay + remainder

total_pay是910。