我正在开展一个项目,我试图从一组125名玩家中选择最佳玩家子集(如下所示)
约束是:
a)球员数量= 3
b)价格总和< = 30
优化函数是Max(投票总和)
Player Vote Price
William Smith 0.67 8.6
Robert Thompson 0.31 6.7
Joseph Robinson 0.61 6.2
Richard Johnson 0.88 4.3
Richard Hall 0.28 9.7
我查看了scipy优化包,但我无法找到将宇宙约束到此子集的任何方法。有没有人能指出我是否有一个可以做到这一点的图书馆? 感谢
答案 0 :(得分:3)
这个问题很适合用数学程序表达,可以用不同的优化库来解决。
它被称为精确的k项背包问题。
例如,您可以使用Package PuLP。它具有与不同优化软件包的接口,但捆绑了一个免费的解算器。
easy_install pulp
免费解算器通常比商业解算器慢,但我认为PuLP应该能够用标准求解器解决问题的合理大型版本。
您的问题可以通过PuLP解决,如下所示:
from pulp import *
# Data input
players = ["William Smith", "Robert Thompson", "Joseph Robinson", "Richard Johnson", "Richard Hall"]
vote = [0.67, 0.31, 0.61, 0.88, 0.28]
price = [8.6, 6.7, 6.2, 4.3, 9.7]
P = range(len(players))
# Declare problem instance, maximization problem
prob = LpProblem("Portfolio", LpMaximize)
# Declare decision variable x, which is 1 if a
# player is part of the portfolio and 0 else
x = LpVariable.matrix("x", list(P), 0, 1, LpInteger)
# Objective function -> Maximize votes
prob += sum(vote[p] * x[p] for p in P)
# Constraint definition
prob += sum(x[p] for p in P) == 3
prob += sum(price[p] * x[p] for p in P) <= 30
# Start solving the problem instance
prob.solve()
# Extract solution
portfolio = [players[p] for p in P if x[p].varValue]
print(portfolio)
使用与Brad Solomon相同的随机数据从125中抽取3名玩家的运行时间为0.5秒。
答案 1 :(得分:1)
由于 a)约束,您的问题是离散优化任务。你应该引入离散变量来表示采取/不采取玩家。请考虑以下Minizinc伪代码:
array[players_num] of var bool: taken_players;
array[players_num] of float: votes;
array[players_num] of float: prices;
constraint sum (taken_players * prices) <= 30;
constraint sum (taken_players) = 3;
solve maximize sum (taken_players * votes);
据我所知,你不能用scipy来解决这些问题(例如this)。
您可以通过以下方式解决问题:
第二种选择似乎对您来说更简单。但是,就个人而言,我更喜欢第一种:它允许您引入各种各样的约束,问题表达感觉更自然和清晰。
答案 2 :(得分:1)
@CaptainTrunky是正确的,scipy.minimize在这里不起作用。
这是一个使用itertools的非常糟糕的解决方法,请忽略其他方法之一是否有效。考虑从125创建3个玩家创建317,750个组合,n!/((n - k)!* k!)。主循环运行时~6m。
from itertools import combinations
df = DataFrame({'Player' : np.arange(0, 125),
'Vote' : 10 * np.random.random(125),
'Price' : np.random.randint(1, 10, 125)
})
df
Out[109]:
Player Price Vote
0 0 4 7.52425
1 1 6 3.62207
2 2 9 4.69236
3 3 4 5.24461
4 4 4 5.41303
.. ... ... ...
120 120 9 8.48551
121 121 8 9.95126
122 122 8 6.29137
123 123 8 1.07988
124 124 4 2.02374
players = df.Player.values
idx = pd.MultiIndex.from_tuples([i for i in combinations(players, 3)])
votes = []
prices = []
for i in combinations(players, 3):
vote = df[df.Player.isin(i)].sum()['Vote']
price = df[df.Player.isin(i)].sum()['Price']
votes.append(vote); prices.append(price)
result = DataFrame({'Price' : prices, 'Vote' : votes}, index=idx)
# The index below is (first player, second player, third player)
result[result.Price <= 30].sort_values('Vote', ascending=False)
Out[128]:
Price Vote
63 87 121 25.0 29.75051
64 121 20.0 29.62626
64 87 121 19.0 29.61032
63 64 87 20.0 29.56665
65 121 24.0 29.54248
... ...
18 22 78 12.0 1.06352
23 103 20.0 1.02450
22 23 103 20.0 1.00835
18 22 103 15.0 0.98461
23 14.0 0.98372