【问题标题】:How to create combinations with constrains in Python如何在 Python 中创建带有约束的组合
【发布时间】:2019-08-24 21:05:14
【问题描述】:

我有一份足球运动员名单。每个球员都有三个属性:位置(G、D、M、A)、姓名和薪水。

Position Name     Salary
P        Buffon    6
D        Maldini  23
D        Baresi   15
D        Bergomi   7
C        Kakà     33
C        Gomez    18
A        Ronaldo  52
A        Vieri    44
...

我想创建一个恰好包含 11 名球员的足球队的所有可能组合。这些是约束:

  1. 玩家限制:元素总数 = 11
  2. 位置约束:P = 1,D = 4,C = 4,A = 2
  3. 薪资约束:总薪资

我已经开始研究,在我看来,itertools 可以帮助我在给定限制的情况下生成所有可能的团队。但我不清楚如何编写约束代码。

subset = df[['position', 'name', 'salary']]
tuples = [tuple(x) for x in subset.values]
all_permutations = itertools.permutations(tuples)
for perm in all_permutations:
    # constrains

【问题讨论】:

  • 这是一个复杂的问题,是否需要从算法的角度“优化”解决?
  • 不,我只需要列出所有可能的排列给定上述约束。对于恰好 11 名玩家的每个组合,我检查所有约束是否正常。如果是,我存储解决方案,否则我丢弃。
  • 我很确定您的意思是组合,而不是排列,因为您将球员放在球队中的顺序无关紧要
  • 你说得对,让我编辑问题
  • 名册规模的范围是什么?还请包括一些您计划用来验证的样本名册。所有球员都是单位置球员吗?

标签: python combinations permutation itertools


【解决方案1】:

itertools 库有一个非常好的函数用于迭代组合。您可以得到 11 名玩家的所有组合,然后对其进行过滤。

from collections import Counter
import itertools

def valid_team(team):
    positions = []
    salary = 0
    for player in team:
        (player_pos,_,player_salary)=player
        positions.append(player_pos)
        salary += player_salary
    pos_count = Counter(positions)
    return (
        pos_count['P'] is 1 and
        pos_count['D'] is 4 and
        pos_count['C'] is 4 and
        pos_count['A'] is 2 and
        salary<200
    )

for team in itertools.combinations(players,11):
    if (valid_team(team)):
        print("found valid team")
        print(team)
    else:
        print("found invalid team")

需要注意的是,上述方法有很多不必要的处理,因为您可以单独选择每个位置。下面的替代实现。

players_sorted = {}
for player in players:
    if player[0] not in players_sorted:
        players_sorted[player[0]] = []
    players_sorted[player[0]].append(player)

p_guys = itertools.combinations(players_sorted['P'],1)
d_guys = itertools.combinations(players_sorted['D'],4)
c_guys = itertools.combinations(players_sorted['C'],4)
a_guys = itertools.combinations(players_sorted['A'],2)

teams = itertools.product(p_guys,d_guys,c_guys,a_guys)

for team in teams:
    team_players = []
    for pos in team:
        team_players.extend(pos)
    if (valid_team(team_players)):
        print("found valid team")
        print(team)
    else:
        print("found invalid team")
        print(team)

【讨论】:

    【解决方案2】:

    您可以使用生成器的简单递归:

    from collections import Counter
    pos_c = {'P':1, 'D':4, 'C':4, 'A':2}
    def teams(d, c = []):
      if len(c) == 11:
        yield c
      else:
        s, pos = sum(int(j) for *_, j in c), Counter([j for j, *_ in c])
        for i in d:
           if not c or (i not in c and s+int(i[-1]) < 200 and pos_c[i[0]] >= pos.get(i[0], 0)+1):
             yield from teams(d, c+[i])
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-11-17
      • 1970-01-01
      • 1970-01-01
      • 2015-03-07
      • 2015-09-11
      • 1970-01-01
      相关资源
      最近更新 更多