1 背景
我们用一个在营销场景下可能用到的例子来进行讲解。很多公司在营销策略中会考虑通过抵扣券发放给用户,进而刺激用户来消费。但是下发卡券是有成本的,一般每次营销预算成本是有限的,公司希望在固定的成本下刺激最多的用户人数去核销。我们就以这样一个场景为例,在有多个约束情况下,通过选择适当数量的卡券分配给各个用户群体,以最大化核销人数。这样的问题我们可以使用线性规划方法求解这个。
2 问题定义
2.1 输入
- A[m][n]: 核销率矩阵,表示第i个用户群在第j张卡券的核销率
- P[m]: 每个人群的总数量
- G: 卡券的总成本预算
- T[n]: 每个卡券的服务类型,假设我们有加油,洗车,充电三种服务,每种服务分别对应的值表示1,2,3,则该数组的每个元素取值范围为{1,2,3}
- R[n]: 每张卡券的面额
- wash_r: 投放给洗车券的人数不少于加油人数的比例
- charge_r:投放给充电券的人数不少于加油人数的比例
2.2 输出
- B[m][n]: 表示在第i个人群中投入第j张卡券的人数数量
2.3 目标
- 最大化核销人数
Maximize Q = ∑ i = 0 m ∑ j = 0 n B [ i ] [ j ] \text{Maximize } Q = \sum_{i=0}^m \sum_{j=0}^n B[i][j] Maximize Q=i=0∑mj=0∑nB[i][j]
3 约束条件
-
人群数量约束
∑ j = 0 n B [ i ] [ j ] ≤ P [ i ] , ∀ i \sum_{j=0}^nB[i][j] \leq P[i], \forall i j=0∑nB[i][j]≤P[i],∀i -
总成本约束
∑ i = 0 m ∑ j = 0 n B [ i ] [ j ] × A [ i ] [ j ] × R [ j ] ≤ G \sum_{i=0}^m \sum_{j=0}^n B[i][j] \times A[i][j] \times R[j] \leq G i=0∑mj=0∑nB[i][j]×A[i][j]×R[j]≤G -
服务类型人数比例约束
计算每个服务的人群 w 1 , w 2 , w 3 , w 4 w_1, w_2, w_3, w_4 w1,w2,w3,w4:
w 1 = ∑ j = 0 n ∑ i = 0 m B [ i ] [ j ] × A [ i ] [ j ] i f T [ j ] = = 1 w_1 = \sum_{j=0}^n\sum_{i=0}^m B[i][j] \times A[i][j] if T[j] ==1 w1=j=0∑ni=0∑mB[i][j]×A[i][j]ifT[j]==1
w 2 = ∑ j = 0 n ∑ i = 0 m B [ i ] [ j ] × A [ i ] [ j ] i f T [ j ] = = 2 w_2 = \sum_{j=0}^n\sum_{i=0}^m B[i][j] \times A[i][j] if T[j] ==2 w2=j=0∑ni=0∑mB[i][j]×A[i][j]ifT[j]==2
w 3 = ∑ j = 0 n ∑ i = 0 m B [ i ] [ j ] × A [ i ] [ j ] i f T [ j ] = = 3 w_3 = \sum_{j=0}^n\sum_{i=0}^m B[i][j] \times A[i][j] if T[j] ==3 w3=j=0∑ni=0∑mB[i][j]×A[i][j]ifT[j]==3
人数比例约束:
w 1 > 0 , w 2 > 0 , w 3 > 0 w_1 >0, w_2>0, w_3>0 w1>0,w2>0,w3>0
w 2 ≥ w a s h _ r × w 1 w_2 \ge wash\_r \times w_1 w2≥wash_r×w1
w 3 ≥ c h a r g e _ r × w 1 w_3 \ge charge\_r \times w_1 w3≥charge_r×w1
4 求解方法
可以==使用线性规划(Linear Programming, LP)或整数线性规划(Integer Linear Programming, ILP)==来求解这个优化问题。考虑到𝐵[i][j]必须是整数(表示人数),ILP 可能是一个更合适的选择。可以使用 Python 的 PuLP 库来求解这个线性规划问题。具体实现代码如下:
################# 输入########################################
#1 核销率矩阵, 这里给出了一个5*3的矩阵,5个人群,在3张卡券的核销率情况
A = [[0.5,0.2,0.4],
[0.3,0.02,0.19],
[0.3,0.6,0.1],
[0.10,0.13,0.55],
[0.2,0.4,0.33]]
#2 每个人群的总数量
P = [10000,2000,500,5000,800]
#3 卡券的总成本数量
G = 15000
#3 卡券服务类型
T = [1,2,3]
#4 每张卡券的面额
R = [5,10,20]
#5 投放洗车的服务人数占比加油人数的比例
wash_r = 0.2
charge_r = 0.1
m = len(P)
n = len(R)
################# 求解########################################
#定义问题
prob = LpProblem("Maximize_Redemption_Rate", LpMaximize)
#定义决策变量
B = [[LpVariable(f'B_{i}_{j}', lowBound=0, cat='Integer') for j in range(n)] for i in range(m)]
#定义目标函数
prob += lpSum(B[i][j] * A[i][j] for i in range(m) for j in range(n))
#添加人群数量约束
for i in range(m):
prob += lpSum(B[i][j] for j in range(n)) <= P[i]
#添加总成本约束
prob += lpSum(B[i][j] * A[i][j] * R[j] for i in range(m) for j in range(n)) <=G
#添加服务类型比例约束
#1 计算w1和w2
w1 = lpSum(B[i][j] for i in range(m) for j in range(n) if T[j] ==1)
w2 = lpSum(B[i][j] for i in range(m) for j in range(n) if T[j]==2)
w3 = lpSum(B[i][j] for i in range(m) for j in range(n) if T[j]==3)
#2 人数约束
prob += w1>=0
prob += w2>=0
prob += w3>=0
prob += w2>=wash_r*w1
prob += w3>=charge_r*w1
#求解问题
prob.solve()
#输出结果
B_optimal = [[B[i][j].varValue for j in range(n)] for i in range(m)]
print(B_optimal)
运行结果如下:
稍微验证了下输出的结果,都符合我们约束的要求。整体程序运行还是非常快的,我们可以根据实际的约束条件,稍微调整下代码就可以了。整体来说还是很方便的。