将数字拆分为可变费率计费频段,即“分层定价”
我有一个纸上简单的问题,但我正在努力解决代码问题。在我走之前,这不是一个字符串分裂问题。将数字拆分为可变费率计费频段,即“分层定价”
我有什么本质上是一个带状时间表应用程序。规模是可变的,基于客户端,但一个例子是在一个月前三个时间是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
,而我在这,我会很感激为我尝试来形容专有名词注释。奥利,如果你在2023年回到这里,下次选择一个更容易的计费方案,队友。
如果我正确理解问题,这里是我的尝试。这不是紧凑的,但我更喜欢可读性。我改变了乐队表示,以[(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
虽然在我的评论,我建议去为最简单/最幼稚的做法,只是通过循环 - 我的好奇心越来越好,所以这里使用递归的方式是“少线”的方法。
从一个“成本桶”列表开始,每个成本桶包含一个小时数(例如[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'
这是一个不灵活的方法,但它会做你特别的事情。 (我应该明确地指出递归的常见问题;如果你想用20,000个成本桶来制定100,000个工作小时计划 - 你可能不想这样做。)
首先,我会变成这样:
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
这里的另一种方法,使用您第一次使用的速率存储方案。 此方法假定在项目上工作的小时数至少与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
看起来像一个延伸到https://stackoverflow.com/questions/28676916/calculate-new-value-based-on-decreasing-value ... –
感谢@ JonClements。它看起来*类似*,但似乎不完全相同。今天我有点疲惫,随时可以在那里纠正我!我已经添加了纸张计算来显示我之后的逻辑。 – Oli
我会建议如果有许多不同的步骤,不同的规则 - 不要试图创建一个通用的“适合所有”解决方案。虽然它可能会使用较少的代码并且感觉“更加灵活”,但是一旦忘记为什么某些规则在那里,它可能最终成为维护噩梦。相反,对于每个支付方案来说,只是一个单独的功能将是最简单的。之后,只需循环一小时,然后计算最后一部分(即幼稚的方式)可能是最好的,因为您可以花更多时间在实际制作产品而不是计算出结算公式。 – Bilkokuya