简体   繁体   English

带有来自不同组的特定数量物品的背包

[英]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。

Full code:完整代码:

  • 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

Explanation:解释:

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_preprocgenerate_groupgroup_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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM