[英]Knapsack with SPECIFIC AMOUNT of items from different groups
So this is a variation of the Knapsack Problem I came with the other day.所以这是我前几天遇到的背包问题的变体。
It is like a 0-1 Knapsack Problem where there are multiple groups and each item belongs to only one group.这就像一个0-1 背包问题,其中有多个组,每个项目只属于一个组。 The goal is to maximize the profits subject to the constraints.
目标是在约束条件下最大化利润。 In this case, a fixed number of items from each group have to be chosen for each group.
在这种情况下,必须为每个组从每个组中选择固定数量的项目。
It is similar to the Multiple Choice Knapsack Problem, but in that case you only pick 1 of item of each group, in this one you want to pick x
amount of items of each group它类似于多项选择背包问题,但在那种情况下,您只能从每组中选择 1 个项目,在这个问题中,您要从每组中选择
x
个项目
So, each item has: value, weight and group所以,每个项目都有:价值,重量和组
Each group has an item count (Ex: if group A (or 0) has 2, the final solution needs to have 2 items of group A, no more no less)每个组都有一个项目数(例如:如果 A 组(或 0)有 2 个,则最终解决方案需要有 2 个 A 组项目,不多也不少)
And and you also have a maximum capacity (not related to the groups)而且你也有最大容量(与组无关)
This translates into:这转化为:
values[i]
= The value of the ith element values[i]
= 第 i 个元素的值weights[i]
= The weigth of the ith element weights[i]
= 第 i 个元素的权重groups[i]
= The group of the ith element groups[i]
= 第 i 个元素的组C
= Capacity C
= 容量n
= Amount of elements n
= 元素数量m
= Amount of groups m
= 组数count[j]
= Amount of items of group j count[j]
= 第 j 组的项目数量I'm attempting a Recursive solution first and then I will try a Dynamic approach.我首先尝试递归解决方案,然后我将尝试动态方法。
Any solution would be appreciated (preferably Python, but anything will do:) ).任何解决方案将不胜感激(最好是 Python,但任何事情都可以:))。
Usefull links I found:我找到的有用链接:
Full code also in: https://github.com/pabloroldan98/knapsack-football-formations完整代码也在: https://github.com/pabloroldan98/knapsack-football-formations
Explanation after the code.代码后的解释。
This code is for an example where you have a Fantasy League with a playersDB
where each player has price (weight), points (value) and position (group);此代码是一个示例,其中您有一个带有玩家数据库的梦幻联盟,其中每个玩家都有价格(权重)、积分(价值)和
playersDB
(组); there is a list of possible_formations
(group variations);有一个
possible_formations
列表(组变体); and a budget
(W) you can't go over.和
budget
(W) 你不能超过 go。
main.py:主要文件:
from group_knapsack import best_full_teams playersDB = [ Player(name="Keylor Navas", price=16, points=7.5, position="GK"), Player(name="Laporte", price=23, points=7.2, position="DEF"), Player(name="Modric", price=22, points=7.3, position="MID"), Player(name="Messi", price=51, points=8.2, position="ATT"), ... ] possible_formations = [ [3, 4, 3], [3, 5, 2], [4, 3, 3], [4, 4, 2], [4, 5, 1], [5, 3, 2], [5, 4, 1], ] budget = 300 best_full_teams(playersDB, possible_formations, budget)
group_knapsack.py: group_knapsack.py:
import itertools from MCKP import knapsack_multichoice_onepick def best_full_teams(players_list, formations, budget): formation_score_players = [] for formation in formations: players_points, players_prices, players_comb_indexes = players_preproc( players_list, formation) score, comb_result_indexes = knapsack_multichoice_onepick( players_prices, players_points, budget) result_indexes = [] for comb_index in comb_result_indexes: for winning_i in players_comb_indexes[comb_index[0]][comb_index[1]]: result_indexes.append(winning_i) result_players = [] for res_index in result_indexes: result_players.append(players_list[res_index]) formation_score_players.append((formation, score, result_players)) print("With formation " + str(formation) + ": " + str(score)) for best_player in result_players: print(best_player) print() print() formation_score_players_by_score = sorted(formation_score_players, key=lambda tup: tup[1], reverse=True) for final_formation_score in formation_score_players_by_score: print((final_formation_score[0], final_formation_score[1])) return formation_score_players def players_preproc(players_list, formation): max_gk = 1 max_def = formation[0] max_mid = formation[1] max_att = formation[2] gk_values, gk_weights, gk_indexes = generate_group(players_list, "GK") gk_comb_values, gk_comb_weights, gk_comb_indexes = group_preproc(gk_values, gk_weights, gk_indexes, max_gk) def_values, def_weights, def_indexes = generate_group(players_list, "DEF") def_comb_values, def_comb_weights, def_comb_indexes = group_preproc( def_values, def_weights, def_indexes, max_def) mid_values, mid_weights, mid_indexes = generate_group(players_list, "MID") mid_comb_values, mid_comb_weights, mid_comb_indexes = group_preproc( mid_values, mid_weights, mid_indexes, max_mid) att_values, att_weights, att_indexes = generate_group(players_list, "ATT") att_comb_values, att_comb_weights, att_comb_indexes = group_preproc( att_values, att_weights, att_indexes, max_att) result_comb_values = [gk_comb_values, def_comb_values, mid_comb_values, att_comb_values] result_comb_weights = [gk_comb_weights, def_comb_weights, mid_comb_weights, att_comb_weights] result_comb_indexes = [gk_comb_indexes, def_comb_indexes, mid_comb_indexes, att_comb_indexes] return result_comb_values, result_comb_weights, result_comb_indexes def generate_group(full_list, group): group_values = [] group_weights = [] group_indexes = [] for i, item in enumerate(full_list): if item.position == group: group_values.append(item.points) group_weights.append(item.price) group_indexes.append(i) return group_values, group_weights, group_indexes def group_preproc(group_values, group_weights, initial_indexes, r): comb_values = list(itertools.combinations(group_values, r)) comb_weights = list(itertools.combinations(group_weights, r)) comb_indexes = list(itertools.combinations(initial_indexes, r)) group_comb_values = [] for value_combinations in comb_values: values_added = sum(list(value_combinations)) group_comb_values.append(values_added) group_comb_weights = [] for weight_combinations in comb_weights: weights_added = sum(list(weight_combinations)) group_comb_weights.append(weights_added) return group_comb_values, group_comb_weights, comb_indexes
MCKP.py: MCKP.py:
import copy def knapsack_multichoice_onepick(weights, values, max_weight): if len(weights) == 0: return 0 last_array = [-1 for _ in range(max_weight + 1)] last_path = [[] for _ in range(max_weight + 1)] for i in range(len(weights[0])): if weights[0][i] < max_weight: if last_array[weights[0][i]] < values[0][i]: last_array[weights[0][i]] = values[0][i] last_path[weights[0][i]] = [(0, i)] for i in range(1, len(weights)): current_array = [-1 for _ in range(max_weight + 1)] current_path = [[] for _ in range(max_weight + 1)] for j in range(len(weights[i])): for k in range(weights[i][j], max_weight + 1): if last_array[k - weights[i][j]] > 0: if current_array[k] < last_array[k - weights[i][j]] + \ values[i][j]: current_array[k] = last_array[k - weights[i][j]] + \ values[i][j] current_path[k] = copy.deepcopy( last_path[k - weights[i][j]]) current_path[k].append((i, j)) last_array = current_array last_path = current_path solution, index_path = get_onepick_solution(last_array, last_path) return solution, index_path def get_onepick_solution(scores, paths): scores_paths = list(zip(scores, paths)) scores_paths_by_score = sorted(scores_paths, key=lambda tup: tup[0], reverse=True) return scores_paths_by_score[0][0], scores_paths_by_score[0][1]
player.py:播放器.py:
class Player: def __init__( self, name: str, price: float, points: float, position: str ): self.name = name self.price = price self.points = points self.position = position def __str__(self): return f"({self.name}, {self.price}, {self.points}, {self.position})" @property def position(self): return self._position @position.setter def position(self, pos): if pos not in ["GK", "DEF", "MID", "ATT"]: raise ValueError("Sorry, that's not a valid position") self._position = pos def get_group(self): if self.position == "GK": group = 0 elif self.position == "DEF": group = 1 elif self.position == "MID": group = 2 else: group = 3 return group
Okay,so I managed to find a solution translating what was here: Solving the Multiple Choice Knapsack Problem from C++
to Python
.好的,所以我设法找到了一个解决方案来翻译这里的内容: Solving the Multiple Choice Knapsack Problem from
C++
to Python
。 My solution also gives the path that got you to that solution.我的解决方案还提供了让您找到该解决方案的途径。 It uses Dynamic Programming and it's very fast.
它使用动态编程,速度非常快。
The input data, instead of having groups[i]
, has the weights and the values as a list of lists, where every list inside represent the values of each group:输入数据没有
groups[i]
,而是将权重和值作为列表列表,其中每个列表代表每个组的值:
weights[i] = [weights_group_0, weights_group_1, ...]
values[i] = [values_group_0, values_group_1, ...]
Where:在哪里:
weights_group_i[j]
= The weigth of the j
th element of the i
th group weights_group_i[j]
= 第i
组第j
个元素的权重values_group_i[j]
= The value of the j
th element of the i
th group values_group_i[j]
= 第i
组第j
个元素的值Those would be the inputs of knapsack_multichoice_onepick
.这些将是
knapsack_multichoice_onepick
的输入。 Here is an example:这是一个例子:
# Example
values = [[6, 10], [12, 2], [2, 3]]
weights = [[1, 2], [6, 2], [3, 2]]
W = 7
print(knapsack_multichoice_onepick(weights, values, W)) # (15, [(0, 1), (1, 1), (2, 1)])
After that I followed @user3386109 's suggestion and did the combinations with the indexes.之后我按照@user3386109 的建议对索引进行了组合。 The group preprocesing methods are
players_preproc
, generate_group
and group_preproc
.组预处理方法是
players_preproc
、 generate_group
和group_preproc
。
Again, this code is for an example where you have a Fantasy League with a playersDB
where each player has price (weight), points (value) and position (group);同样,此代码是一个示例,其中您有一个带有
playersDB
的 Fantasy League,其中每个玩家都有价格(权重)、点数(价值)和 position(组); there is a list of possible_formations
(group variations);有一个
possible_formations
列表(组变体); and a budget
(W) you can't go over.和
budget
(W) 你不能超过 go。
The best_full_teams
method prints everything and uses all the previous ones. best_full_teams
方法打印所有内容并使用之前的所有内容。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.