有限预算分配下的01背包问题

有限预算的权益分配本质上是个升级版的背包问题。假设总预算为 C C C,用户 i i i在券 j j j下的核销率是 p i j p_{ij} pij,发券面额是 c i j c_{ij} cij,我们的求解目标是总预算约束下的订单最大化:

m a x ∑ i , j x i j p i j  s.t.  x i j ∈ { 0 , 1 } ∑ j x i j = 1 ∑ i , j x i j c i j ≤ C max \sum_{i,j} x_{ij} p_{ij} \\ \begin{aligned} \text { s.t. } x_{ij} & \in \{0, 1\} \\ \sum_{j} x_{ij} &= 1 \\ \sum_{i,j} x_{ij} c_{ij} &\leq C \\ \end{aligned} maxi,jxijpij s.t. xijjxiji,jxijcij{0,1}=1C

将上述业务问题抽象成01背包问题就是,在背包容量限制下的物品价值最大化。但传统的背包问题对应的是给同一个用户发多张券,而营销场景则是给多个用户分别只发一张券,相当于二维化传统背包问题了。

定义 d p [ i ] [ j ] [ k ] dp[i][j][k] dp[i][j][k]为在预算 k k k下给用户 i i i发放券 j j j后的累计最大订单量,那么动态转移方程如下:

d p [ i ] [ j ] [ k ] = m a x ( d p [ i − 1 ] [ : J ] [ : k ] ) + p i j dp[i][j][k] = max(dp[i-1][:J][:k])+p_{ij} dp[i][j][k]=max(dp[i1][:J][:k])+pij

其中 J J J表示券的总数,是个枚举值。

代码示例:

def max_value(users_coupons, C):
    # 用户数
    user_num = len(users_coupons)
    # 券数
    coupon_num = len(users_coupons[0])

    dp = []
    for i in range(user_num):
        tmp = [[float("-inf")] * (C + 1) for _ in range(coupon_num)]
        dp.append(tmp)

    # dp[i][j][k]:在预算k下,给用户i发放券j下的的累计最大订单
    # 第1个用户初始化
    for j in range(coupon_num):
        for k in range(1, C + 1):
            if users_coupons[0][j][0] <= k:
                dp[0][j][k] = users_coupons[0][j][1]

    for i in range(1, user_num):
        for j in range(coupon_num):
            for k in range(1, C + 1):
                # 发券成本,核销率
                cost, cvr = users_coupons[i][j]
                # 说明此刻预算能给用户i发放券j
                if cost <= k:
                    # 发放券j后,剩余的预算
                    gap = k - cost
                    # 遍历上一个用户的所有可能发放券,获取剩余预算下的最大订单量
                    for prev_j in range(coupon_num):
                        prev_max_value = max(dp[i - 1][prev_j][:gap + 1])
                        dp[i][j][k] = max(prev_max_value + cvr, dp[i][j][k])

    ret = float("-inf")
    for j in range(coupon_num):
        ret = max(max(dp[user_num - 1][j]), ret)

    return ret


if __name__ == "__main__":
    users_coupons = [
        [[2, 0.3], [4, 0.6], [1, 0.2]],
        [[5, 0.1], [3, 0.8], [2, 0.6]],
        [[7, 0.3], [8, 1], [3, 0.9]]
    ]
    C = 15
    ret = max_value(users_coupons, C)
    print("max accumulate cvr: {}".format(ret))

备注:上述代码实现的时空复杂度过高,一天的预算都有几个亿,不可能初始化这么大的数组,且寻找最优解耗时也长。业界对于此营销问题的解决方案都是走运筹,具体见:线上运筹优化公式推导

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值