【发布时间】:2019-07-09 15:01:55
【问题描述】:
我有一个订单,其中包含行数和折扣,需要在这些行之间按行成本按比例分配。
我不是数学家,所以我会介绍这个符号来解释这个案例。订单有N 行,商品价格为Pi,商品数量为Qi,总行成本为Ti,其中Ti = Qi * Pi。总订单价格为T = sum(Ti)。算法需要分配折扣D,结果是Di 的列表 - 为每个订单行分配折扣。
结果必须满足以下条件:
-
D = sum(Di):线路折扣之和必须等于原始折扣 -
Di%Qi = 0:折扣必须能被数量整除,没有余数 -
Di <= Ti:折扣不能超过总线路成本-
Di/D ~ Ti/T:折扣尽可能按比例分配
-
输入数据满足以下谓词:
-
D <= T,折扣不超过总订单成本 -
D、Di和Qi是整数值,Pi是十进制值 - 输入数据的变化不能满足要求的条件。例如,3 行,每行有 3 个价格为 10 的商品,输入折扣为 10 (
N=3; Qi=3; Pi=10; D=10)。无法将其分配为可除以行数。在这种情况下,算法应该返回错误,其中包含无法分配的折扣量(对于我的示例,它是 1)
现在我们的算法实现是这样的(F#上的简化版)
type Line = {
LineId: string
Price: decimal
Quantity: int
TotalPrice: decimal
Discount: decimal
}
module Line =
let minimumDiscount line =
line.Quantity
|> decimal
|> Some
|> Option.filter (fun discount -> discount <= line.TotalPrice - line.Discount)
let discountedPerItemPrice line = line.Price - line.Discount / (decimal line.Quantity)
let spread discount (lines: Line list) =
let orderPrice = lines |> List.sumBy (fun l -> l.TotalPrice)
let preDiscountedLines = lines |> List.map (fun line ->
let rawDiscount = line.TotalPrice / orderPrice * discount
let preDiscount = rawDiscount - rawDiscount % (decimal line.Quantity)
{line with Discount = preDiscount})
let residue = discount - List.sumBy (fun line -> line.Discount) preDiscountedLines
let rec spreadResidue originalResidue discountedLines remainResidue remainLines =
match remainLines with
| [] when remainResidue = 0m -> discountedLines |> List.rev |> Ok
| [] when remainResidue = originalResidue -> sprintf "%f left to spread" remainResidue |> Error
| [] -> discountedLines |> List.rev |> spreadResidue remainResidue [] remainResidue
| head :: tail ->
let minimumDiscountForLine = Line.minimumDiscount head
let lineDiscount = minimumDiscountForLine
|> Option.filter (fun discount -> discount <= remainResidue)
|> Option.defaultValue 0m
let discountedLine = {head with Discount = head.Discount + lineDiscount}
let discountedLines = discountedLine :: discountedLines
let remainResidue = remainResidue - lineDiscount
spreadResidue originalResidue discountedLines remainResidue tail
spreadResidue residue [] residue preDiscountedLines
该算法采用了here 中的一些解决方案,适用于大多数情况。 但是,它在以下情况下失败:
P1=14.0; Q1=2;
P2=11.0; Q2=3;
D=52
至少存在一种可能的分布:D1=22; D2=30,但当前算法无法发现它。那么什么是更好的传播算法或更好的传播残差算法?
【问题讨论】: