如何计算满足三类约束条件的每日权重(Python实现)
如何计算满足三类约束条件的每日权重(Python实现)
我明白你的需求啦——要找到一组严格递减、和为7的每日权重,还要恰好让10个用户里的7个加权平均低于11。前两个约束你已经用指数衰减+归一化搞定了,但第三个约束确实需要结合起来处理,下面我给你两种可行的Python实现思路:
方法一:约束优化法(基于Scipy)
这个方法把问题转化为带约束的优化问题,通过变量替换把单调性约束转化为变量边界,再用目标函数逼近“恰好7个用户达标”的要求。
步骤1:准备数据
首先先把你的数据集固定下来(加随机种子方便复现):
import pandas as pd import numpy as np from scipy.optimize import minimize # 固定随机种子,复现你的数据集 np.random.seed(42) df = pd.DataFrame(np.random.randint(8, 15, size=(10, 7))) df.columns = ['day_0', 'day_-1', 'day_-2', 'day_-3', 'day_-4', 'day_-5', 'day_-6'] df.index.name = 'id' df = df.reset_index() # 提取用户消费数据(去掉id列) X = df.drop('id', axis=1).values
步骤2:变量替换处理单调性约束
为了方便处理w0 > w1 > ... > w6 >0的约束,我们做个变量替换:
- 令
s6 = w6(最小的权重,正数) s5 = w5 - w6(正数,保证w5>w6)s4 = w4 -w5(正数,保证w4>w5)- ...以此类推,直到
s0 =w0 -w1(正数)
这样所有s0~s6都是正数,且权重和为7的约束可以转化为:s0 +2s1 +3s2 +4s3 +5s4 +6s5 +7s6 =7。接下来写个转换函数把s转成最终的权重w:
def s_to_w(s): s0, s1, s2, s3, s4, s5, s6 = s w6 = s6 w5 = s5 + w6 w4 = s4 + w5 w3 = s3 + w4 w2 = s2 + w3 w1 = s1 + w2 w0 = s0 + w1 return np.array([w0, w1, w2, w3, w4, w5, w6])
步骤3:定义目标函数与约束
目标函数要让“加权平均低于11的用户数”尽可能接近7,同时可以加个小惩罚项避免加权和刚好卡在阈值上:
def objective(s): w = s_to_w(s) # 加权平均 <11 等价于 加权和 < 11*7=77(因为权重和为7) weighted_sums = X @ w # 统计达标用户数 cnt_less = np.sum(weighted_sums < 77) # 目标:让达标数尽可能接近7,平方差最小化 # 加边界惩罚项,避免加权和刚好卡在77附近 boundary_penalty = np.sum(np.exp(-(weighted_sums - 77)**2 / 10)) return (cnt_less -7)**2 + 0.1 * boundary_penalty
然后定义约束条件和变量边界:
# 权重和为7的等式约束 cons = ({'type': 'eq', 'fun': lambda s: s[0] + 2*s[1] + 3*s[2] + 4*s[3] + 5*s[4] + 6*s[5] +7*s[6] -7}) # 所有s变量必须为正(用1e-6避免数值问题) bounds = [(1e-6, None) for _ in range(7)]
步骤4:运行优化并验证结果
# 初始化变量(可以用随机值或指数衰减权重转化的s) s_init = np.ones(7) * 0.1 # 用SLSQP算法求解带约束的优化问题 result = minimize(objective, x0=s_init, method='SLSQP', bounds=bounds, constraints=cons) # 提取最优权重 w_opt = s_to_w(result.x) # 验证结果 weighted_sums = X @ w_opt cnt_less = np.sum(weighted_sums <77) print("=== 优化结果 ===") print(f"满足加权平均<11的用户数:{cnt_less}") print(f"权重:{np.round(w_opt,4)}") print(f"权重严格递减:{all(w_opt[i] > w_opt[i+1]+1e-6 for i in range(6))}") print(f"权重和:{np.sum(w_opt):.4f}")
如果一次优化没得到恰好7个用户的结果,可以多试几次不同的初始值,因为目标函数是离散的,可能会陷入局部最优。
方法二:随机搜索法(简单易上手)
如果觉得优化算法太复杂,也可以用“生成符合前两个约束的权重→验证第三个约束”的随机搜索法,虽然笨但有效:
实现代码
def generate_valid_weights(): """生成满足w0>w1>...>w6>0且sum(w)=7的权重""" while True: # 生成7个随机正数,降序排序后归一化到和为7 w = np.random.rand(7) w = -np.sort(-w) # 降序排序 w = w / np.sum(w) *7 # 检查严格单调性(避免相等的情况) if all(w[i] > w[i+1]+1e-6 for i in range(6)) and w[-1]>1e-6: return w # 搜索符合条件的权重 target_cnt =7 found = False print("正在随机搜索符合条件的权重...") while not found: w = generate_valid_weights() weighted_sums = X @w cnt_less = np.sum(weighted_sums <77) if cnt_less == target_cnt: found = True print("\n=== 找到符合条件的权重 ===") print(f"满足加权平均<11的用户数:{cnt_less}") print(f"权重:{np.round(w,4)}") print(f"权重严格递减:{all(w[i] > w[i+1]+1e-6 for i in range(6))}") print(f"权重和:{np.sum(w):.4f}")
这种方法的好处是不需要理解复杂的优化理论,代码简单,而且对于7维的情况,搜索速度其实挺快的,一般几秒内就能找到符合条件的权重。
备注:内容来源于stack exchange,提问作者quant
相关产品推荐
相关产品推荐

