我有一个有点复杂,或者也许并不复杂的问题,但是长的问题是,我将尝试将其归纳为基本部分,然后也许我可以从那里弄清楚。

所以我想做的是填满一个棒球名册。我有一个球员列表,然后根据位置分为几个其他列表。从这一点上来说,我想用所有可能的球员组合来填补名单。我已经写了一个完成此任务的基本脚本,但是由于 list 很大,因此简单地遍历每个组合都不理想。我只是用一部分玩家来测试它,但仍然花费了大约一个半小时。

有些球员有资格被列入名单中的多个位置,因此,一些名字将出现在我从中提取名字的两个或多个列表中。我第一次节省时间,尝试检查每个迭代是否将包含重复项,如果包含重复项,请跳过该播放器。

import os, csv, time

player_pool_csv = open('Available Player Pool.csv', 'r') player_pool = csv.reader(player_pool_csv) player_pool = list(player_pool)

roster = ['a','b','c','d','e','f','g','h']
# roster with characters is my solution to the TypeError: unhashable type: 'list'
# when I check len(roster) against len(set(roster)) in the first loop

catcher_pool = []
first_pool = []
second_pool = []
third_pool = []
short_pool = []
of_pool = []

for player in player_pool:
    if 'C' in player[2]:
        catcher_pool.append(player)
    if '1B' in player[2]:
        first_pool.append(player)
    if '2B' in player[2]:
        second_pool.append(player)
    if '3B' in player[2]:
        third_pool.append(player)
    if 'SS' in player[2]:
        short_pool.append(player)
    if 'OF' in player[2]:
        of_pool.append(player)

start = time.time()

for catcher in catcher_pool:
roster[0] = catcher[0]
if len(roster) != len(set(roster)):
    continue
for first_baseman in first_pool:
    roster[1] = first_baseman[0]
    if len(roster) != len(set(roster)):
        continue
    for second_baseman in second_pool:
        roster[2]= second_baseman[0]
        if len(roster) != len(set(roster)):
            continue
        for third_baseman in third_pool:
            roster[3] = third_baseman[0]
            if len(roster) != len(set(roster)):
                continue
            for shortstop in short_pool:
                roster[4] = shortstop[0]
                if len(roster) != len(set(roster)):
                    continue
                for outfielder1 in of_pool:
                    roster[5] = outfielder1[0]
                    if len(roster) != len(set(roster)):
                        continue
                    for outfielder2 in of_pool:
                        roster[6] = outfielder2[0]
                        if len(roster) != len(set(roster)):
                            continue
                        for outfielder3 in of_pool:
                            roster[7] = outfielder3[0]
                            if len(roster) != len(set(roster)):
                                continue
                            print(roster)

end = time.time()
elapsed = end - start
print(elapsed)

当我运行此代码时,它似乎在“for outfielder2”循环中自行停止。我似乎无法确切地指出为什么它停止时会停止,以及如何修复它。

我知道您可能需要更多的信息,例如它正在吸引玩家的池中内容,但是我不想首先证明这个问题是否不必要。如果需要,我会把这些东西放进去。

有什么想法我做错了什么,以及如何使其更有效?谢谢。

编辑

好吧,我将player_pool缩减为
['Ender Inciarte', '0.283', 'OF', '3900']
['A.J. Pollock', '0.304', 'OF', '4900']
['Jamie Romak', '0.349', 'OF', '2000']
['Adam Jones', '0.258', 'OF', '3700']
['Paul Goldschmidt', '0.343', '1B', '5600']
['Chris Davis', '0.306', '1B', '4300']
['Aaron Hill', '0.245', '2B/3B', '2400']
['Jimmy Paredes', '0.276', '1B/2B', '3000']
['Jake Lamb', '0.283', '3B', '3700']
['Manny Machado', '0.315', '3B', '4400']
['Welington Castillo', '0.31', 'C', '3800']
['Caleb Joseph', '0.266', 'C', '3200']
['Xander Bogaerts', '0.318', '3B/SS', '4300']
['Eugenio Suarez', '0.294', 'SS', '3000']

当我在注释掉所有if len(roster) != len(set(roster))的情况下运行代码时,它正确地返回了每个组合(我写入其中的每个计时器花费48.3秒)。但是,如果我把if len(roster) != len(set(roster))重新放回去,这是输出:
['Welington Castillo', 'Paul Goldschmidt', 'Aaron Hill', 'Jake Lamb', 'Xander Bogaerts', 'Ender Inciarte', 'A.J. Pollock', 'Jamie Romak']
['Welington Castillo', 'Paul Goldschmidt', 'Aaron Hill', 'Jake Lamb', 'Xander Bogaerts', 'Ender Inciarte', 'A.J. Pollock', 'Adam Jones']
['Welington Castillo', 'Paul Goldschmidt', 'Aaron Hill', 'Jake Lamb', 'Xander Bogaerts', 'Ender Inciarte', 'Jamie Romak', 'A.J. Pollock']
['Welington Castillo', 'Paul Goldschmidt', 'Aaron Hill', 'Jake Lamb', 'Xander Bogaerts', 'Ender Inciarte', 'Jamie Romak', 'Adam Jones']
0.03500199317932129

最佳答案

您可以通过已在外部循环中选择的播放器过滤内部循环的池来进行很多优化。这对于套子特别容易。
您也可以将itertools中的combinations用于外场球员,以避免另外三个循环。
结果看起来像这样:

import itertools as it
import time

player_pool = [['Ender Inciarte', '0.283', 'OF', '3900'],
               ['A.J. Pollock', '0.304', 'OF', '4900'],
               ['Jamie Romak', '0.349', 'OF', '2000'],
               ['Adam Jones', '0.258', 'OF', '3700'],
               ['Paul Goldschmidt', '0.343', '1B', '5600'],
               ['Chris Davis', '0.306', '1B', '4300'],
               ['Aaron Hill', '0.245', '2B/3B', '2400'],
               ['Jimmy Paredes', '0.276', '1B/2B', '3000'],
               ['Jake Lamb', '0.283', '3B', '3700'],
               ['Manny Machado', '0.315', '3B', '4400'],
               ['Welington Castillo', '0.31', 'C', '3800'],
               ['Caleb Joseph', '0.266', 'C', '3200'],
               ['Xander Bogaerts', '0.318', '3B/SS', '4300'],
               ['Eugenio Suarez', '0.294', 'SS', '3000']]

# create a player hash for later use, I hope player names are unique
player_hash = {p[0]: p for p in player_pool}

# use sets for the pools so that we can use set difference later on
catcher_pool = set()
first_pool = set()
second_pool = set()
third_pool = set()
short_pool = set()
of_pool = set()

# fill the pools only with player names
for player, stats in player_hash.items():
    if 'C' in stats[2]:
        catcher_pool.add(player)
    if '1B' in stats[2]:
        first_pool.add(player)
    if '2B' in stats[2]:
        second_pool.add(player)
    if '3B' in stats[2]:
        third_pool.add(player)
    if 'SS' in stats[2]:
        short_pool.add(player)
    if 'OF' in stats[2]:
        of_pool.add(player)

start = time.time()
playing = set()
all_rosters = []
roster = [None]*8

# create a little generator that only yields players from a pool
# which are currently not playing
def filtered(pool):
    for player in pool-playing:
        playing.add(player)
        yield player
        playing.remove(player)

with open('rosters.txt', 'w') as f:
    for catcher in filtered(catcher_pool):
        roster[0] = catcher
        for first_baseman in filtered(first_pool):
            roster[1] = first_baseman
            for second_baseman in filtered(second_pool):
                roster[2] = second_baseman
                for third_baseman in filtered(third_pool):
                    roster[3] = third_baseman
                    for shortstop in filtered(short_pool):
                        roster[4] = shortstop
                        for outfielders in it.combinations(of_pool-playing, 3):
                            roster[5:8] = outfielders
                            # append result to a list instead of printing
                            # this is a lot faster
                            all_rosters.append(roster[:])
                            # if our list is bigger than 1e6, write it to file
                            if len(all_rosters) > 10**6:
                                f.writelines(repr(roster)+'\n'
                                             for roster
                                             in all_rosters)
                                all_rosters = []
    # don't forget to write the last batch which did not reach 1e6
    f.writelines(repr(roster)+'\n' for roster in all_rosters)

end = time.time()
elapsed = end - start

# make sure no roster contains double names
assert all(len(set(roster)) == len(roster) == 8 for roster in all_rosters)

# make sure all rosters are unique
assert len(all_rosters) == len(set(tuple(roster) for roster in all_rosters))

print(len(all_rosters))
print(elapsed)

打印:
[a lot of rosters]
232
0.00205016136169

但是结果是,而不是。我猜这不是问题。如果要对它们进行排序,只需在生成后对all_rosters进行排序。

10-04 16:23